Because the vault relies on a keeper to finalize asynchronous steps, there is no on-chain mechanism to force or time-limit completion of a pending deposit/withdrawal. As a result, a malicious or absent keeper can indefinitely block a user’s deposit or withdrawal from finalizing, effectively locking user funds.
RootCause & Where It Happens
Deposit Flow when the position is already open.
In deposit(uint256 amount)
, if positionIsClosed == false
, the function sets:
The user’s deposit is now in a pending state, waiting for the keeper to perform the INCREASE_ACTION
(i.e. to open or enlarge the GMX position).
Withdraw Flow when curPositionKey != bytes32(0)
.
In withdraw(...)
, if there is an open GMX position, the vault calls:
The code only partially prepares for withdrawal. The final step _withdraw()
or _handleReturn()
will occur after the keeper finalizes the flow (e.g., calling runNextAction()
).
In both scenarios, the user is stuck until the keeper (who is msg.sender == keeper
) calls the subsequent steps (runNextAction()
, etc.) to finalize the flow. If the keeper never does so, the user cannot withdraw or reinitiate other actions.
The contract enforces a strict flow != FLOW.NONE
check, meaning while flow
is in DEPOSIT
or WITHDRAW
, no one else (including the user) can override or forcibly finalize it.
The cancelFlow()
function is also restricted by _onlyKeeper()
.
Therefore, the user cannot “time out” the operation or force a revert to a clean state.
Example ->
User deposits into the vault while a position is open; deposit()
transitions flow
to FLOW.DEPOSIT
and sets nextAction.selector = INCREASE_ACTION
.
The keeper disappears or becomes malicious and never calls the runNextAction()
function to actually mint shares and finalize.
The user can do nothing further. Because flow
is no longer FLOW.NONE
, any subsequent deposit or withdrawal attempts will revert with Error.FlowInProgress()
.
Their funds stay locked in the contract’s internal accounting, with no on-chain recourse to retrieve them.
**Severity Based on likelyhood & impact **
This is a medium to high severity design flaw: user funds can be held hostage by an unresponsive keeper or any scenario in which the keeper is offline, misconfigured, or malicious.
It contradicts the usual DeFi principle that users should have a guaranteed on-chain path to reclaim their assets if no further actions are taken by external actors.
Recommended Remediation
Introduce a Time-Out or Force-Finish Mechanism
For example, if flow
remains DEPOSIT
or WITHDRAW
for more than X
blocks or seconds, allow the depositor to forcibly cancel or finalize their flow themselves.
This can be implemented either by letting the user call cancelFlow()
after a grace period, or providing a separate “emergency finalize” that reverts the pending action and frees user funds.
Multiple Keepers / Distributed System
The protocol can mitigate some risk by having multiple keeper addresses or an open keeper network, ensuring that at least one honest keeper finalizes flows. However, without an on-chain fallback, a sufficiently large keeper cartel or partial centralization still poses a risk.
Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.
Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point. Keepers are added by the admin, there is no "malicious keeper" and if there is a problem in those keepers, that's out of scope. ReadMe and known issues states: " * System relies heavily on keeper for executing trades * Single keeper point of failure if not properly distributed * Malicious keeper could potentially front-run or delay transactions * Assume that Keeper will always have enough gas to execute transactions. There is a pay execution fee function, but the assumption should be that there's more than enough gas to cover transaction failures, retries, etc * There are two spot swap functionalies: (1) using GMX swap and (2) using Paraswap. We can assume that any swap failure will be retried until success. " " * Heavy dependency on GMX protocol functioning correctly * Owner can update GMX-related addresses * Changes in GMX protocol could impact system operations * We can assume that the GMX keeper won't misbehave, delay, or go offline. " "Issues related to GMX Keepers being DOS'd or losing functionality would be considered invalid."
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.
Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.
Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point. Keepers are added by the admin, there is no "malicious keeper" and if there is a problem in those keepers, that's out of scope. ReadMe and known issues states: " * System relies heavily on keeper for executing trades * Single keeper point of failure if not properly distributed * Malicious keeper could potentially front-run or delay transactions * Assume that Keeper will always have enough gas to execute transactions. There is a pay execution fee function, but the assumption should be that there's more than enough gas to cover transaction failures, retries, etc * There are two spot swap functionalies: (1) using GMX swap and (2) using Paraswap. We can assume that any swap failure will be retried until success. " " * Heavy dependency on GMX protocol functioning correctly * Owner can update GMX-related addresses * Changes in GMX protocol could impact system operations * We can assume that the GMX keeper won't misbehave, delay, or go offline. " "Issues related to GMX Keepers being DOS'd or losing functionality would be considered invalid."
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.