DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Invalid

[M-01] Missing Market Decrease Order Handling in `afterOrderCancellation()` May Leave Positions in an Inconsistent State

Summary

The afterOrderCancellation() function in the contract PerpetualVault.sol is responsible for handling failed order executions in GMX. However, the function does not handle MarketDecrease orders properly. If a decrease order fails, the function does not attempt to retry or update the vault state accordingly. This could leave the contract in an inconsistent state, where an attempted position closure does not actually occur, potentially leading to stuck funds or unintended exposure to market risk.

Vulnerability Details

The function processes failed orders based on their OrderType. It handles failed MarketSwap orders by setting the nextAction.selector to SWAP_ACTION and handles deposits and withdrawals by attempting to retry them. However, there is no explicit handling for failed MarketDecrease orders, which means that if a decrease order fails, the contract does not retry or register the failure correctly.

Relevant code snippet:

function afterOrderCancellation(
bytes32 requestKey,
Order.OrderType orderType,
IGmxProxy.OrderResultData memory orderResultData
) external {
if (msg.sender != address(gmxProxy)) {
revert Error.InvalidCall();
}
_gmxLock = false;
if (orderResultData.isSettle) {
nextAction.selector = NextActionSelector.SETTLE_ACTION;
} else if (orderType == Order.OrderType.MarketSwap) {
nextAction.selector = NextActionSelector.SWAP_ACTION;
nextAction.data = abi.encode(swapProgressData.remaining, swapProgressData.isCollateralToIndex);
} else {
if (flow == FLOW.DEPOSIT) {
nextAction.selector = NextActionSelector.INCREASE_ACTION;
nextAction.data = abi.encode(beenLong);
} else if (flow == FLOW.WITHDRAW) {
nextAction.selector = NextActionSelector.WITHDRAW_ACTION;
} else {
delete flowData;
delete flow;
}
}
emit GmxPositionCallbackCalled(requestKey, false);
}

Issue Explanation

  • Missing Handling for MarketDecrease Orders: If an order intended to reduce or close a position fails, the function does not set nextAction.selector to retry the decrease or adjust the contract state.

  • This can result in stuck positions, where a liquidation or partial closure was expected but never executed due to an error in GMX.

  • Incorrect flow handling: While deposits, withdrawals, and swaps are handled, failed decrease orders are ignored, making them a blind spot in position management.

Impact

  • Positions may remain open when they should be closed, leading to unintended exposure to market risk.

  • Funds may be stuck if a decrease order was supposed to return collateral but failed silently.

  • Inconsistent vault state: Since MarketDecrease failures are not registered, other contract functions may assume a position is closed when it is still active.

  • Potential loss of funds if liquidation thresholds are not met due to an unprocessed decrease order.

Tools Used

  • Manual Code Review

Recommendations

  1. Explicitly handle failed MarketDecrease orders by setting nextAction.selector = NextActionSelector.DECREASE_ACTION; to retry or manage failures properly.

  2. Ensure vault state updates correctly when a decrease order fails. Add a fallback mechanism to reattempt closure or at least log the failure for manual intervention.

  3. Emit a specific event for failed MarketDecrease orders, so that off-chain monitoring can detect and handle such failures properly.

Proposed Fix:

Modify the afterOrderCancellation() function:

} else if (orderType == Order.OrderType.MarketDecrease) {
// Handle failed MarketDecrease orders properly
nextAction.selector = NextActionSelector.DECREASE_ACTION;
nextAction.data = abi.encode(beenLong);
}

This ensures that the next action will correctly attempt to close or decrease the position again rather than leaving it in a broken state.

Updates

Lead Judging Commences

n0kto Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Suppositions

There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.

Support

FAQs

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