GMXProxy
Design flaw exists in its management of the global order queue. Specifically, the contract uses a single OrderQueue struct to store both the order’s unique request key and an isSettle flag that indicates whether the order is a settlement order. Because new orders simply overwrite this global queue without verifying that a previous order has been fully processed, concurrent or rapidly submitted orders can cause the isSettle flag to be overwritten. This leads to a situation where the callback for an earlier order may be processed with the wrong flag, causing the PerpetualVault to misinterpret the order type and update its state incorrectly.
Order Creation in GMXProxy:
When a regular order is created via createOrder, the global queue is set with:
Later, when a settlement order is created via settle, the same global queue is overwritten:
No check is made to ensure that an active order (Order A) has been completed before overwriting the global queue.
Callback Handling:
In afterOrderExecution (and similarly in afterOrderCancellation), the contract reads the current value of queue.isSettle and includes it in the order result data sent to PerpetualVault. If Order A’s callback is executed after Order B has been submitted, the callback for Order A will erroneously use isSettle = true (from Order B), misrepresenting Order A as a settlement order.
The root cause of the vulnerability is that the GMXProxy contract relies on a single, global OrderQueue struct for all order submissions without binding the isSettle flag to a unique order identifier. In effect, if multiple orders are initiated in close succession—either accidentally or through attacker‐induced race conditions—the global state is overwritten. There is no on‑chain mechanism (e.g., a mapping from request keys to order metadata) to ensure that callbacks are processed against the vault state snapshot corresponding to the order’s creation.
Order A (Regular Order) Submission:
PerpetualVault calls createOrder, setting:
queue.requestKey = keyA
queue.isSettle = false
Order A is recorded as a regular (non-settlement) order.
Order B (Settlement Order) Submission:
Before Order A’s callback is processed, PerpetualVault calls settle, which overwrites the global queue with:
queue.requestKey = keyB
queue.isSettle = true
Callback for Order A:
GMX later triggers the callback for Order A.
The callback function in GMXProxy uses the current global queue, finding requestKey = keyB and isSettle = true.
As a result, the PerpetualVault receives data indicating that Order A was a settlement order, leading to incorrect state updates (for example, marking a position as settled when it is not) or misallocation of fees.
State Inconsistency: The PerpetualVault may misinterpret the type of order it is processing. This can result in incorrect collateral adjustments, fee misallocation, or errors in share minting.
Financial Loss: Persistent misinterpretation of order types could allow an attacker—or even result from a coding error—to “game” the vault state, leading to extraction or diversion of funds.
Invariant Violation: The protocol’s core invariants regarding fair and consistent fee accounting and state management are violated.
To mitigate this vulnerability, the order tracking should be redesigned so that each order’s isSettle flag is stored independently:
Mapping Approach:
Replace the single global queue with a mapping keyed by the unique order request key:
Then, in createOrder and settle, store:
Finally, in callback functions (e.g., afterOrderExecution), retrieve the order’s flag using the unique request key:
Alternative Approach:
Enforce single-order processing by checking that no order is currently active before allowing a new one. However, this may restrict concurrent order handling, so the mapping approach is preferable.
High – This vulnerability directly impacts the integrity of order processing, which may lead to misallocation of funds or incorrect state transitions in the protocol.
Below is a Foundry PoC that simulates the vulnerability by using simplified versions of the GMXProxy and PerpetualVault components. The PoC demonstrates that when a regular order is submitted (with isSettle = false) and then a settlement order is submitted (with isSettle = true), the global queue is overwritten. Consequently, a callback for the first order incorrectly interprets the order type.
src/FakeGmxProxy.solsrc/FakeVault.soltest/FakeVaultTest.t.sol
From the project root, run:
The test should pass—demonstrating that when Order B (settlement order) is submitted before Order A’s callback, the global queue’s isSettle flag is overwritten. As a result, the callback for Order A misinterprets the order as a settlement order.
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.
Order is proceed one by one and requestKey is only used to cancelOrder. I didn’t see any real scenario where it will cause a problem. Flow and gmxLock will prevent that to happen.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.