Skip to content
$1,000,000 in security audit grants are live now, Apply here →

Security review · July 2025

V2.2 Crosschain, Part 5

for GMX

GMX engaged Guardian to review the security of their GMX Crosschain architecture. From the 23rd of June to the 2nd of July, a team of 6 auditors reviewed the source code in scope.

Published
Review window
June 23 to July 2, 2025
Language
Solidity
Chains
Arbitrum, Avalanche
Sector
Perpetuals
  • 0 Critical
  • 1 High
  • 4 Medium
  • 14 Low
  • 0 Informational

9 resolved · 10 acknowledged

Scope

Overview

GMX engaged Guardian to review the security of their GMX Crosschain architecture. From the 23rd of June to the 2nd of July, a team of 6 auditors reviewed the source code in scope.

Findings 19

  1. H-01 High Bridged Withdrawals Fail Logical Error Resolved
    Location
    ControllerUtils.sol: 63

    Description

    GM and GLV withdrawals result in two output tokens, and the bridgeOutFromController function is called twice to bridge these tokens. Both calls to bridgeOutFromController use the same withdrawal.dataList.

    The dataList contains the Stargate provider address, and that provider can only bridge a single specific token, which is stargate.token(). Therefore, it is not possible to bridge both outputToken and secondaryOutputToken using the same dataList.

    Recommendation

    One option to consider is including two different providers in the dataList during cross-chain withdrawals.

    However, this would require different dataList decoding logic for deposits and withdrawals, as deposits require bridging only a single token.

    Another option is to enforce outputToken = secondaryOutputToken when the user wants to withdraw and bridge out.

    Resolution

    GMX Team: Resolved.

  2. M-01 Medium Price Impact Factor Gaming Gaming Acknowledged
    Location
    Global

    Description

    Because the price impact is no longer capped to the amount available in the impact amalgam, but rather the maximum lent, it is possible that net positive impact is paid out to traders from the market up to the magnitude of the max lent.

    This could arise and be potentially forced through a gaming of the price impact factors as they change based upon the market depth.

    A sophisticated actor could observe that the market depth off-chain is now lower and open trades before the price impact factors are updated to bank a X amount of negative impact for creating an imbalance of A.

    The sophisticated actor could then observe the price impact factors by GMX being updated and realize Y amount of positive impact for closing their position and removing the imbalance of A.

    Recommendation

    Be aware of this risk, to mitigate its probability the risk oracle price impact factor changes should not be large in magnitude compared to their previous factors. Furthermore be sure to keep the max lent usd values small so that the opportunity is limited.

    Resolution

    GMX Team: Acknowledged.

  3. M-02 Medium Incorrect Position Key Used Logical Error Resolved
    Location
    OrderUtils.sol: 209

    Description

    In the _updatePositionLastSrcChainId function the swap path is evaluated to find the collateral token of the position.

    However for decrease orders this will produce an inaccurate result as the swap path has nothing to do with the collateral token of the position.

    Recommendation

    Only evaluate the resulting token from the order swap path for increase orders and use the initialCollateralToken for decrease orders.

    Resolution

    GMX Team: Resolved.

  4. M-03 Medium Incorrect Gas Utils Function Logical Error Resolved
    Location
    LayerZeroProvider.sol: 471

    Description

    The _handleGlvWithdrawal in LayerZeroProvider.sol uses _validateGasLeft to validate if the gas left is sufficient to cover the remaining GLV withdrawal creation flow.

    However, it uses the GasUtils function estimateCreateGlvDepositGasLimit instead of the estimateCreateGlvWithdrawalGasLimit.

    This can lead to incorrect gas validation and potentially causing the known compose censoring issue.

    Recommendation

    Use the correct GasUtils function for the _handleGlvWithdrawal flow:

    estimateCreateGlvWithdrawalGasLimit

    Resolution

    GMX Team: Resolved.

  5. M-04 Medium Same Actions Cannot Be Done Validation Resolved
    Location
    BaseGelatoRelayRouter.sol: 415

    Description

    The nonce was removed from the relayParams to fix a previous issue, and the replay check is now performed based on the digest.

    However, since there is no unique identifier, the structHash and the digest will be identical for the exact same actions.

    This will result in the same signature being generated, causing the action to fail even if the user legitimately intends to perform it a second time.

    Users must change something in the signature if they want to perform the same action with the same parameters.

    The easiest option appears to be the deadline, allowing users to repeat the same action by signing with a different deadline value.

    Recommendation

    Document this behavior for users and inform them about how digests are generated. Alternatively, consider adding a user-provided salt to the relayParams to allow differentiation of actions, even when all other parameters are identical.

    Resolution

    GMX Team: Resolved.

  6. L-01 Low Lent Payback Is Not Rounded Up Rounding Resolved
    Location
    PositionImpactPoolUtils.sol

    Description

    In the reduceLentAmount function the longTokenAmount and shortTokenAmount computed to be paid by the caller is rounded down. Instead this amount should be rounded up to round in the favor of the GM market.

    Recommendation

    Consider rounding the longTokenAmount and shortTokenAmount values up to round in favor of the GM market.

    Resolution

    GMX Team: Resolved.

  7. L-02 Low Config Keeper May Init An Invalid Provider Unexpected Behavior Resolved
    Location
    Config.sol: 59

    Description

    In the initOracleProviderForToken function there is no validation that the provider is enabled with the isOracleProviderEnabledKey.

    Furthermore there is no validation that the token is not address(0).

    Recommendation

    Consider validating that the provider is enabled with the isOracleProviderEnabledKey and that the token is not address(0).

    Resolution

    GMX Team: Resolved.

  8. L-03 Low Provider May Be Immediately Updated Unexpected Behavior Acknowledged
    Location
    Config.sol

    Description

    In the initOracleProviderForToken function the oracleProviderUpdatedAt value is not assigned, therefore the config keeper may immediately invoke the setOracleProviderForToken function to configure a new provider.

    Recommendation

    Consider updating the oracleProviderUpdatedAt value in the initOracleProviderForToken function.

    Resolution

    GMX Team: Acknowledged.

  9. L-04 Low Config Keeper Trusted To Set Providers Trust Assumptions Acknowledged
    Location
    Config.sol

    Description

    In the setOracleProviderForToken function the config keeper is able to reset the oracle provider for a token that is currently configured.

    As a result a compromised Config Keeper may assign a supported provider that is incompatible with the specified token in order to DoS trades for a period of time and potentially carry out risk free trades or avoid liquidation.

    It may also be possible to use a feed which reports an inaccurate price for the token in order to game the exchange.

    Recommendation

    Consider if the Config Keeper should be trusted to set the provider for an already configured token. If not, then consider only allowing the oracle provider for an already configured token to be configured through the time lock.

    Resolution

    GMX Team: Acknowledged.

  10. L-05 Low Lacking Error Validation Validation Resolved
    Location
    RelayUtils.sol

    Description

    In the validateSignature function, during the second attempt at signature verification the error returned by the tryRecover function is not validated to be the ECDSA.RecoverError.NoError result.

    Recommendation

    Out of an abundance of caution, consider validating that the error from the second tryRecover invocation is the ECDSA.RecoverError.NoError result.

    Resolution

    GMX Team: Resolved.

  11. L-06 Low Bridging Fee Is Paid Twice Logical Error Resolved
    Location
    GlvWithdrawalUtils.sol, ExecuteWithdrawalUtils.sol

    Description

    Two cross-chain bridging transactions occur during cross-chain withdrawals, regardless of the output tokens, and both transactions incur a bridging fee.

    However, if outputToken = secondaryOutputToken, there's no need to bridge twice. Instead, the total output amount can be bridged in a single transaction, avoiding double fees.

    Recommendation

    Bridge outputAmount + secondaryOutputAmount in a single transaction if the output tokens are the same.

    Resolution

    GMX Team: Resolved.

  12. L-07 Low Documentation Regarding Withdrawals Documentation Acknowledged
    Location
    GlvWithdrawalUtils.sol, ExecuteWithdrawalUtils.sol

    Description

    Cross-chain withdrawals attempt to bridge both outputToken and secondaryOutputToken. However, there is no guarantee that these tokens are supported by Stargate, as the Stargate currently supports only a limited set of major tokens.

    As a result, users attempting to bridge out the resulting output tokens may encounter unexpected failures.

    Recommendation

    Document which output tokens are supported by Stargate for bridging, and clearly inform users of these limitations.

    Resolution

    GMX Team: Acknowledged.

  13. L-08 Low Increased Signature Protection Warning Acknowledged
    Location
    RelayUtils.sol

    Description

    In the validateSignature function the minified digest is not marked as used, based on the idea that the original digest being marked as used is sufficient.

    However there is some nonzero chance that a malicious actor is able to find a collision with the minified digest that allows the same signature to be submitted and successfully authenticated on behalf of the victim.

    Recommendation

    While the likelihood of this being a viable attack vector is extremely small, out of an abundance of caution the minifiedDigest which was signed should be marked as used as well.

    This way if a collision were to ever occur it would simply prevent a user from making a certain signature, versus allowing for an unexpected action to be carried out on behalf of the user.

    Resolution

    GMX Team: Acknowledged.

  14. L-09 Low Execution Cost Greater Than Provided Fee Configuration Acknowledged
    Location
    general.ts

    Description

    During withdrawal creation, the handler verifies if the wnt tokens sent by the user are enough to cover the estimated gas spent during execution.

    For withdrawals, this estimation uses the an estimated gas limit of 1_500_000 set in the config, plus some base amount and adjustments.

    However, the withdrawal execution now includes two optional bridge out flows, that will break the gas limit estimation. Therefore, keepers will need to spend more gas than the execution fee supplied by the user, so they will prefer not to execute these.

    The same applies to the glv withdrawal flow. Additionally, this may pose a notable issue on chains with lower block gas limits such as avalanche, in some cases opening up opportunities for risk free trades.

    Recommendation

    During our internal testing, the gas spent to execute a withdrawal with bridge out flows is above 2_700_000. We advice to conduct some testing with different params and increase both the withdrawalGasLimit and glvWithdrawalGasLimit to a more suitable value.

    Resolution

    GMX Team: Acknowledged.

  15. L-10 Low Missing Gas Limit Config Params Configuration Acknowledged
    Location
    general.ts

    Description

    The LayerZeroProvider now allows users to initiate a GM or GLV withdrawal order creation. These new flows also include the _validateGasLeft to prevent a previous issue regarding lzCompose censoring.

    However, neither CREATE_WITHDRAWAL_GAS_LIMIT or CREATE_GLV_DEPOSIT_GAS_LIMIT are set in general config, so these values are 0 in the dataStore.

    This opens up the risk of an actor invoking the lzCompose function, providing insufficient amount of gas and forcing the withdrawal transaction to fail.

    Recommendation

    Add the missing keys to the general config and be sure to include them in the updateGeneralConfigUtils script.

    Resolution

    GMX Team: Acknowledged.

  16. L-11 Low Order Cancels Do Not Update lastSrcChainId Logical Error Acknowledged
    Location
    Global

    Description

    The lastSrcChainId is updated on order creation, however when an order is canceled it is not considered for a lastSrcChainId update.

    Recommendation

    Include _updatePositionLastSrcChainId at the end of the cancel order flow.

    Resolution

    GMX Team: Acknowledged.

  17. L-12 Low Loss Of Signature Cancelability And Sequentiality Warning Acknowledged
    Location
    BaseGelatoRelayRouter.sol: 411-412

    Description

    As a fix for using nonces in non-atomic actions (crosschain), GMX switched to using digests to protect against replays.

    However, this approach is applied globally across all actions, not just cross-chain ones. This comes with two implications:

    1. Users can no longer cancel a signature by issuing a new one with the same nonce—unlike

    standard EVM-style signature workflows. A signed message remains valid until its deadline.

    1. At time T=0, if a user signs multiple actions, they are no longer processed sequentially, which

    could be important in some trade setups.

    Recommendation

    If these trade-offs are acceptable, consider enforcing short deadlines close to the current timestamp. Otherwise, consider separating cross-chain non-atomic logic from the rest of the gasless/multichain action set.

    Resolution

    GMX Team: Acknowledged.

  18. L-13 Low Unnecessary Reordering Of Transfer Steps Best Practices Acknowledged
    Location
    MultichainGlvRouter.sol: 49-50

    Description

    To support multichain fee payments for atomic actions from lzCompose, GMX reordered _processTransferRequests to come before the WNT transfer.

    However, this is no longer necessary, as the early return for excluded addresses in handleRelayFee was removed. This allows users to send tokens directly to the router when needed.

    Recommendation

    While there is no harm in the current order as well, we just want to make GMX aware that they can use handleRelayFee to transfer tokens to router similar to all gasless actions, and don’t have to use transfer requests specifically.

    Resolution

    GMX Team: Acknowledged.

  19. L-14 Low Bridging Out Tokens From GLV Vault Warning Resolved
    Location
    GlvWithdrawalUtils.sol: 283

    Description

    User may optionally bridge out tokens after withdrawing from GLV. However, the executeGlvWithdrawal uses the srcChainId, receiver and dataList from the glvWithdrawal struct.

    If receiver = glvWithdrawal.glv(), srcChainId = 0 and dataList contains the bridge action data, this will start a bridge out using the glv as the account.

    Although user will lose all of its funds plus a previous wnt deposit to pay for the bridge fee, this is an unexpected scenario that might become significant if the glv address will eventually have non-zero multichain balance.

    Recommendation

    Consider passing an empty dataList array and srcChainId = 0, just like it's done in the executeGlvDeposit flow.

    Resolution

    GMX Team: Resolved.

More from GMX

All 44 reports
  1. Timelock Updates

    4 findings 4 findings: 3 low, 1 informational
  2. LayerZeroProvider Routing

    1 finding 1 finding: 1 medium
  3. Open Interest Updates

    5 findings 5 findings: 2 medium, 3 low
  4. Updates Branch

    2 findings 2 findings: 2 low

Put your code through the same review.

This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.

Get a quote