Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: high
Invalid

Withdraw & Trade Race Condition

summary

A user could withdraw collateral right before executing createMarketOrder.

Vulnerability details

The race condition occurs because the contract validates the user’s margin balance before actually actually finalizing the trade

  1. Attacker calls createMarketOrder

  • The function first fetches the attacker’s margin balance

  • The function validates that the user has enough collateral to cover the required margin.

  1. Before the trade is finalized, the Attacker Withdraws Funds

  • Since the function does not lock collateral, the attacker can call a separate function that can withdraw the collateral in the same transaction (flashbot/private mempool) or in the next block.

  • This reduces the account balance below the required margin after validation but before trade execution

  1. Trade Still Executes Without Sufficient Collateral

  • The order continues execution based outdated margin validation results.

  • Since margin is no longer available the position is now under-collateralized or completely unbacked

  • This can trigger unfair liquidations, or worse. let the attacker executes a trade without actually posting any margin.

Meanwhile, the liquidationKeeper contract is responsible for automating liquidations in the perpsEngine. However, it's current design allow traders to manipulate their margin balances leading to potential exploits

The checkUpkeep() function still sees it as solvent since margin was valid at trade time

But by the time performUpkeep() executes their margin is too low

They avoid liquidation temporarily, potentially allowing bad debt building

(
ctx.marginBalanceUsdX18, // Fetches margin balance
ctx.requiredInitialMarginUsdX18,
ctx.requiredMaintenanceMarginUsdX18,
ctx.orderFeeUsdX18,
ctx.settlementFeeUsdX18,
) = simulateTrade(
SimulateTradeParams({
tradingAccountId: params.tradingAccountId,
marketId: params.marketId,
settlementConfigurationId: SettlementConfiguration.MARKET_ORDER_CONFIGURATION_ID,
sizeDelta: params.sizeDelta
})
);
// Race Condition Risk: Margin is checked here, but funds are not locked!
tradingAccount.validateMarginRequirement(
ctx.requiredMarginUsdX18,
ctx.marginBalanceUsdX18,
ctx.orderFeeUsdX18.add(ctx.settlementFeeUsdX18).add(
ud60x18(perpsEngineConfiguration.liquidationFeeUsdX18)
)
);

Impact

  • Breaking the system logic

  • Users can execute trade under-collateralized

Tools Used

Manual Review

Recommendations

Lock Collateral until Trade execution completes
Only release the collateral after successful trade execution.

Recheck margin balances inside performUpkeep()and checkUpkeep()

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Out of scope

Support

FAQs

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