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

PerpetualVault Can Mismatch Internal Position Flags with GMX Causing Blocked User Actions and Stuck Funds

Brief

The PerpetualVault contract’s _updateState function allows the internal position status flags (positionIsClosed, beenLong) to become permanently out of sync with the actual position state tracked in curPositionKey through GMX. When orders fail or are canceled, _updateState sets flags without verifying if the position truly closed. This mismatch between internal flags and the reality in GMX creates an inconsistent contract state that can disrupt valid user actions and open unintended attack surfaces.

Details

The issue comes from the lack of validation between local flags and actual position status. In _updateState, the contract sets positionIsClosed independently of whether curPositionKey is fully closed on GMX:

function _updateState(bool _positionIsClosed, bool _isLong) internal {
if (flow == FLOW.SIGNAL_CHANGE) {
positionIsClosed = _positionIsClosed; // updated with no verification
if (_positionIsClosed == false) {
beenLong = _isLong;
}
}
// ...
}

Meanwhile, upon order failure, afterOrderCancellation clears the flow data but never reverts positionIsClosed to its accurate state:

function afterOrderCancellation(
bytes32 requestKey,
Order.OrderType orderType,
IGmxProxy.OrderResultData memory orderResultData
) external {
_gmxLock = false;
if (flow == FLOW.SIGNAL_CHANGE) {
delete flowData;
delete flow;
}
// No mechanism to restore positionIsClosed or check if curPositionKey remains active
}

Because the contract never cross-checks whether GMX truly closed the position, the asynchronous nature of external keepers can cause the local positionIsClosed flag to remain “true” while a real GMX position is still active. Subsequent logic that depends on positionIsClosed or beenLong will operate under false assumptions, potentially blocking deposits, invalidating withdrawals, or applying incorrect fee calculations.

Specific Impact

This vulnerability allows the PerpetualVault contract to operate with an invalid assumption of position state, leading to blocked user actions, stuck funds, and misapplied fees. In the worst case, positions may remain active yet be treated as closed by the contract, introducing a severe threat to the protocol’s integrity and user assets.

Updates

Lead Judging Commences

n0kto Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Design choice
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.

invalid_liquidation_do_not_reset_positionIsClosed

"// keep the positionIsClosed value so that let the keeper be able to create an order again with the liquidated fund" Liquidation can send some remaining tokens. No real impact here.

Appeal created

itsgreg Submitter
9 months ago
n0kto Lead Judge
9 months ago
n0kto Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Design choice
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.

invalid_liquidation_do_not_reset_positionIsClosed

"// keep the positionIsClosed value so that let the keeper be able to create an order again with the liquidated fund" Liquidation can send some remaining tokens. No real impact here.

Support

FAQs

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

Give us feedback!