GmxProxy contract allows multiple pending orders to be created simultaneously, violating the requirement that only one order should be in progress at a time. This breaks the order queue management system and could lead to order execution conflicts. When a keeper triggers order creation through the PerpetualVault, the GmxProxy's queue tracking gets out of sync because it doesn't enforce the single pending order constraint.
This is similar to a ticket booking system allowing multiple reservations for the same seat while each booking appears valid in isolation, it creates conflicts at execution time.
The GmxProxy.sol implementation only tracks orders through: GmxProxy.sol#L75
The issue is that createOrder() doesn't verify if there's already a pending order before creating a new one. The queue struct gets overwritten rather than enforcing exclusivity.
The trace is like this: PerpetualVault.run() → GmxProxy.createOrder() → queue update → No uniqueness check
The current implementation allows a dangerous scenario, multiple orders can be created without checking if there's already one in progress. Each new order simply overwrites the queue, breaking the fundamental assumption that orders are processed sequentially. Consider what happens when a vault tries to adjust positions during volatile market conditions:
Vault A initiates a position increase
Before that order executes, Vault B initiates another order
The queue gets overwritten, losing track of Vault A's order
Both vaults have paid execution fees, but only one order will actually execute
The execution fees (typically 0.002 ETH per order) are lost for any overwritten orders, and position updates become unpredictable. This directly impacts the vault's ability to maintain its target leverage ratio. The protocol's keeper system is explicitly designed for sequential order processing
The GmxProxy contract serves as a critical bridge in the protocol's architecture, managing all interactions with GMX's perpetual trading system. At its peek, it's designed to handle leveraged trading positions up to 3x, with each vault maintaining consistent leverage throughout its lifecycle.
The vulnerability emerges in the order queue management system. When a vault initiates a trade through GmxProxy, the contract should act like a meticulous traffic controller, ensuring only one trade passes through at a time. However, the current implementation lacks this crucial check.
Unfolds in the createOrder() function: GmxProxy#createOrder
Each new order blindly overwrites the queue, breaking the protocol's fundamental assumption of sequential order processing. This isn't just a theoretical concern, it directly impacts the vault's ability to maintain its target leverage ratio, which is a core invariant of the system.
The consequences are precise and measurable:
Execution fees of 0.002 ETH are lost for each overwritten order
Position updates become unpredictable, potentially deviating from the vault's specified leverage (1x-3x range)
The keeper system's asynchronous action processing gets disrupted
Think of it like a vending machine that accepts multiple payments simultaneously but only dispenses one item, the additional payments are lost while the machine's inventory state becomes unclear.
The assumption that the PerpetualVault's flow control (_gmxLock) would prevent concurrent orders. However, this overlooks:
The keeper system's asynchronous nature (documented in PerpetualVault.sol comments)
GMX's own order execution delays
The possibility of multiple vaults sharing a GmxProxy (though not recommended)
Direct Impact:
Lost execution fees (0.002 ETH per overwritten order)
Position updates becoming unpredictable
Leverage ratios deviating from targets
Consider enforcing the protocol's single-order invariant at the contract level, aligning with the keeper system's design philosophy of sequential, deterministic order processing.
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.