Users lose their funds if the refundExecutionFee
function fails in the try-catch block.
In the _cancelFlow
functions with is meant to cancel the flow of the vault, if the current flow is WITHDRAW
, then users are supposed to be refunded their execution fee via the refundExecutionFee
function in the try-catch block. as shown below
The try section calls the refundExecutionFee
function in the GMX proxy contract, which uses the payable(receipient).transfer(amount)
line to refund the execution fees, since the transfer
has limited gas and is likely to fail under certain circumstances, if it fails the _cancelFlow
moves on with the execution of the rest of the functions thus leading to the user losing their funds. The refund has a high likelyhood of failing doue to the use of .transfer()
to handle the eth transfer.
The issue in the _cancelFlow
function causes users to permanently lose their execution fee refunds if the refundExecutionFee
call to the GmxProxy
contract fails. The try-catch block will silently ignores failures of the payable().transfer()
operation, which is prone to failure due to its fixed 2300 gas stipend—insufficient for recipients with complex fallback logic or under gas-intensive conditions. When this happens during a WITHDRAW
flow, the function proceeds to set flow = FLOW.LIQUIDATION
without reverting or retrying, effectively erasing the refund opportunity. This results in a direct financial loss for users, as their entitled funds are neither returned nor held for later recovery, undermining the contract’s reliability and user trust.
Manual Review
Revert on Refund Failure in _cancelFlow
Remove the empty catch {} block and let the transaction revert if refundExecutionFee fails. This ensures that the flow cancellation halts if the refund cannot be processed, preventing the loss of user funds by keeping the contract state intact for retry or manual intervention.
Replace .transfer()
with .call()
in refundExecutionFee
to reduce the likelihood of a transfer Failing.
Modify the IGmxProxy contract’s refundExecutionFee function to use payable(recipient).call{value: amount}("") instead of payable(recipient).transfer(amount). The .call() method forwards all available gas (configurable if needed) and is less likely to fail due to gas limitations, unlike .transfer()’s fixed 2300 gas stipend.
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.
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.