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

Security review · February 2025

Gasless Transactions

for GMX

GMX engaged Guardian to review the security of their Gasless transactions support. From the 27th of January to the 3rd of February, a team of 4 auditors reviewed the source code in scope.

Published
Review window
January 27 to February 3, 2025
Language
Solidity
Chains
Arbitrum
Sector
Perpetuals
  • 0 Critical
  • 5 High
  • 4 Medium
  • 18 Low
  • 0 Informational

15 resolved · 12 acknowledged

Scope

Overview

GMX engaged Guardian to review the security of their Gasless transactions support. From the 27th of January to the 3rd of February, a team of 4 auditors reviewed the source code in scope.

Issues Detected Throughout the engagement 5 High/Critical issues were uncovered and promptly remediated by the GMX team.

Findings 27

  1. H-01 High Atomic Swaps With Normal Fees Gaming Resolved
    Location
    BaseGelatoRelayRouter.sol: 202

    Description

    The introduction of fee swaps to pay the executor allows for users to do arbitrarily large swaps in a single transaction without first initiating this action on chain.

    Before the introduction of gasless transactions this ability was not allowed on the GMX Exchange and it was explicitly disallowed for atomic withdrawals to include swaps.

    Gasless transactions unintentionally introduces a way for users to achieve single transaction swaps by using a higher than needed fee swap input amount and receiving the excess amount back.

    Furthermore, the swap made in this instance uses the normal swap fees as computed by the SwapUtils.swap function. There is no application of the AtomicSwap fee pricing.

    Recommendation

    Consider removing the fee swap feature so that the ability for users to perform swaps in a single transaction is not unintentionally introduced with this update.

    Otherwise, keep the feature and limit the size of swap that is allowed to be carried out from this, and furthermore charge the higher AtomicSwap fee rate for these swaps.

    Resolution

    GMX Team: Resolved.

  2. H-02 High Missing Update Order Fee Logic Logical Error Resolved
    Location
    BaseGelatoRelayRouter.sol: 106

    Description

    In the _updateOrder function there is no logic to support sending wnt tokens to the order vault to cover any fee increase related to the update of an order.

    This means that any order updates which result in a higher executionFee being required will not be possible through gasless transactions.

    Recommendation

    Include logic to transfer additional executionFee wnt value to the OrderVault to be able to successfully update orders which would have an increased executionFee.

    Resolution

    GMX Team: Resolved.

  3. H-03 High Missing decreasePositionSwapType Validation Logical Error Resolved
    Location
    GelatoRelayRouter.sol: 134

    Description

    In the _getCreateOrderStructHash the decreasePositionSwapType value is not included in the resulting hash and therefore cannot be validated.

    The caller of the createOrder function can then decide the value of the _getCreateOrderStructHash on the params.

    This can be used to cause loss or unexpected outcomes for the user as their position and integrating addresses may not have support for a particular receiving token.

    Recommendation

    Include the params.decreasePositionSwapType in the _getCreateOrderStructHash function.

    Resolution

    GMX Team: Resolved.

  4. H-04 High Lacking Cancellation Receiver Validation Validation Resolved
    Location
    SubaccountRouter.sol: 96

    Description

    The SubaccountRouter contract lacks validation for the cancellation receiver in the createOrder function.

    This way sub accounts can potentially create orders with a large initialCollateralDeltaAmount on them and cancel them with the cancellationReceiver as their own address to drain the main account's assets.

    Recommendation

    Consider adding the cancellationReceiver validation to the createOrder function in the SubaccountRouter contract.

    Resolution

    GMX Team: Resolved.

  5. H-05 High callbackContract Siphons Refunds Validation Resolved
    Location
    SubaccountGelatoRelayRouter.sol: 79

    Description

    In the createOrder function the receiver and cancellationReceiver addresses are validated against the account to ensure that funds are not sent to an address that is not controlled by the position owner.

    However there is no validation on the callback contract which will take a higher priority than the cancellationReceiver or receiver when the executionFee is refunded.

    Recommendation

    If it is important for the protocol to ensure that sub accounts cannot siphon the refund fee this way, consider validating that the provided callback contract is the saved callback contract for the account and market.

    Otherwise be aware of this issue and clearly document it for users and integrators of the sub account feature.

    Resolution

    GMX Team: Resolved.

  6. M-01 Medium removeSubaccount Cannot Be Gasless Logical Error Resolved
    Location
    SubaccountGelatoRelayRouter.sol: 141

    Description

    The removeSubaccount function is not outfitted to be a gasless transaction function. The function is missing the _handleRelay logic and does not have a onlyGelatoRelay modifier.

    Recommendation

    Outfit the removeSubaccount function so that it can be used by the Gelato relayer in a gasless manner.

    Resolution

    GMX Team: Resolved.

  7. M-02 Medium Unused Permit DoS DoS Resolved
    Location
    BaseGelatoRelayRouter.sol: 271

    Description

    In the _handleTokenPermits function if the existing allowance for the spender is greater than the required amount then the permit signature is not used.

    However this allows for a malicious DoS in the future because the permit signature is now public and the corresponding nonce has not been used.

    The next time the same signer wishes to use the corresponding nonce for a permit to the same token a malicious actor can submit the permit they signed in the past to use the corresponding nonce before the user's newly signed permit is executed.

    This can be carried out for future interactions with the GMX system or any other permit application for the token.

    Recommendation

    Consider always using the permit signature no matter if it is unnecessary and instead determine if the permit is necessary at the UI level.

    Resolution

    GMX Team: Resolved.

  8. M-03 Medium subAccount Is Not Fully Refunded In Top Up Logical Error Acknowledged
    Location
    SubaccountRouter.sol: 222

    Description

    When determining how much to top up it the contract calculates the amount of gas used as follows:

    uint256 nativeTokensUsed = (startingGas - gasleft()) * tx.gasprice + executionFee

    The issue with this is that there is ample logic and gas consumed after this calculation. None of which will be considered for refund. Meaning that a sub account will always get an insufficient refund in terms of the additional top up logic.

    Recommendation

    Add a topUpgas variable to the calculation which contains the amount of gas that will be consumed after the calculation.

    Resolution

    GMX Team: Acknowledged.

  9. M-04 Medium Swap Occurs Without Market Update Logical Error Acknowledged
    Location
    BaseGelatoRelayRouter.sol: 216

    Description

    Before any action is executed that a user can take GMX will call updateFundingAndBorrowingState to update the borrowing rate of traders in the underlying pool.

    However currently a swap through the gelato routers will occur without making any update which means that subsequent borrowing fee rate calculations are incorrect between the period of the swap and the next position update. Causing an incorrect amount of borrowing fees to be charged.

    Recommendation

    Consider updating the borrowing and funding state with updateFundingAndBorrowingState before a swap occurs in a market.

    Resolution

    GMX Team: Acknowledged.

  10. L-01 Low Subaccounts Access To Funds Are Not Partitioned Unexpected Behavior Acknowledged
    Location
    Global

    Description

    An account can have many sub accounts all of which will have access to the same funds. Because of this users may end up having more funds traded then expected.

    Recommendation

    Consider giving the user control over how much a sub account can spend, or document to users that all sub accounts share the same total allowance

    Resolution

    GMX Team: Acknowledged.

  11. L-02 Low Comingling Of Gelato And GMX Fee Logical Error Acknowledged
    Location
    BaseGelatoRelayRouter.sol 300

    Description

    The execution fee and gelato fee come from the same source, fee.feeAmount. That means if a user wants to use USDC to pay gelato when creating an order they will have to pay a (swap) fee to pay a (gelato) fee.

    It also means that executionFee for GMX will not be sufficient at times since for createOrder it is just sending over the residual amount, so even in cases where there is no swap there is still the case where Gelato fee increases and reduces the residual amount that can be used for GMX executionFee.

    Recommendation

    Consider separating the execution fee and the gelato fee so that users can provide wnt for the execution fee without paying a fee on it.

    Resolution

    GMX Team: Acknowledged.

  12. L-03 Low Unused Enum Superfluous Code Resolved
    Location
    Errors.sol: 426

    Description

    In the Errors.sol file the SignatureType enum is not used throughout the codebase.

    Recommendation

    Remove the unused SignatureType or implement it's intended use-case.

    Resolution

    GMX Team: Resolved.

  13. L-04 Low Lacking Feature Validation Validation Resolved
    Location
    Global

    Description

    There is currently no feature to deactivate gasless transactions in the event that they need to be.

    Recommendation

    Consider adding a gasless transactions wide feature that can be disabled, and when disabled no gasless transactions can be executed.

    Resolution

    GMX Team: Resolved.

  14. L-05 Low Permit Frontrunning Frontrunning Resolved
    Location
    BaseGelatoRelayRouter.sol: 274

    Description

    In the _handleTokenPermits function a permit is made to an arbitrary token. However since the permit signature must exist in the transaction calldata it is visible in the mempool for chains like Avalanche which have a public mempool.

    This allows malicious actors to frontrun the relayer and submit this permit in a separate transaction, causing the relayer's execution of the action to fail.

    Recommendation

    Either ensure that the relayer is using a private RPC or consider adding logic to continue if the permit action fails, since in the case where a malicious actor has frontrun the permit the approved value has already been given.

    Resolution

    GMX Team: Resolved.

  15. L-06 Low Bypassing Subaccount Disabled Feature Logical Error Acknowledged
    Location
    SubaccountGelatoRelayRouter.sol: 157

    Description

    The SubaccountGelatoRelayRouter allows users to create, update and cancel orders using a subaccount and relaying transaction with Gelato. All subaccount GMX interactions are done directly using the SubaccountUtils, skipping the SubaccountRouter.

    During _handleSubaccountAction, it will validate if the subaccount feature is enabled for the SubaccountGelatoRelayRouter.

    Therefore, if the sub account feature is disabled for SubaccountRouter, it will still be available using. SubaccountGelatoRelayRouter. This can create unexpected scenarios, if both features are not disabled at the same time.

    Recommendation

    Document this scenario and make sure that both subaccount features are disabled at the same time.

    Resolution

    GMX Team: Acknowledged.

  16. L-07 Low Missing Swap Path Validation Validation Resolved
    Location
    BaseGelatoRelayRouter.sol

    Description

    The _swapFeeTokens function does not perform any validation on the length of the swap path provided. This does not adhere to the maximum swap length allowed on the GMX exchange for canonical orders.

    Recommendation

    Consider validating the swap path in the _swapFeeTokens function to avoid any unexpected behaviors.

    Resolution

    GMX Team: Resolved.

  17. L-08 Low Missing MaxFee Validation For Relayer Validation Resolved
    Location
    BaseGelatoRelayRouter.sol: 307

    Description

    The router contracts will pay the Gelato relayer a fee during _transferRelayFee. To ensure there is a heightened control over the fees, Gelato suggests to use _transferRelayFeeCapped with a maxFee that will prevent transactions to be executed if the relayer fee is above a certain limit.

    Recommendation

    Introduce a maxFee parameter, either in the function call, or as a state variable, and use _transferRelayFeeCapped instead.

    Resolution

    GMX Team: Resolved.

  18. L-09 Low removeSubaccount Marked As Payable Modifiers Resolved
    Location
    SubaccountGelatoRelayRouter.sol: 145

    Description

    The payable modifier is added to the removeSubaccount function, however this function does not do anything with the accepted Ether value.

    Recommendation

    Remove the payable modifier from the removeSubaccount function.

    Resolution

    GMX Team: Resolved.

  19. L-10 Low Duplicated orderKey Events Unexpected Behavior Resolved
    Location
    BaseGelatoRelayRouter.sol

    Description

    In the swap for the fees for _swapFeeTokens the orderKey provided is the same as the order key that will be created.

    The order that is created could also be a swap order and thus there may be confusion about which events correspond to the order execution and which correspond to the fee swap for the order.

    Recommendation

    Confirm whether or not this is expected behavior. If it is not or the behavior should be made more clear, consider assigning a different order key which is unique to, but different from, the order being swapped for in the fee swapped.

    Resolution

    GMX Team: Resolved.

  20. L-11 Low Nonce Dependence Prevents Subsequent Actions DoS Acknowledged
    Location
    Global

    Description

    In the Relay Signature verification logic for _validateCall and _handleSubaccountApproval a monotonically increasing nonce is used to validate actions by a signer.

    However since the message nonce execution must be in order if multiple messages were to be signed and one message were to fail execution then it would prevent all subsequent messages from being executed.

    The message could fail execution for a number of reasons including but not limited to a deadline invalidation, a swap failure, or an errantly missing approval.

    Recommendation

    Consider using unique nonces which are not required to be monotonically increasing so that if one message fails it does not prevent messages signed with a higher nonce from being executed.

    Resolution

    GMX Team: Acknowledged.

  21. L-12 Low Redundant Required Fee Check Superfluous Code Acknowledged
    Location
    BaseGelatoRelayRouter.sol: 304

    Description

    The contract validates if the requiredRelayFee sent by Gelato Router is greater than the output amount returned from swapFeeTokens.

    However, the function will still revert without that validation, during _transferRelayFee during the token transfer if the relay params did not specify the correct amount.

    Similarly, if the remaining fee tokens (residual) are not enough to cover the executionFee, the transaction will revert in the orderHandler call.

    Recommendation

    Avoid redundant token balance checks as they are already handled during transfer functions.

    Resolution

    GMX Team: Acknowledged.

  22. L-13 Low Missing validateMarketTokenBalance Validation Validation Acknowledged
    Location
    BaseGelatoRelayRouter.sol: 202

    Description

    In the _swapFeeTokens function there is no validateMarketTokenBalance validation after the swap. However this validation is performed throughout the codebase whenever a swap occurs to validate that no market balances were perturbed by a swap.

    Recommendation

    Add validateMarketTokenBalance validation after the swap is invoked in the _swapFeeTokens function.

    Resolution

    GMX Team: Acknowledged.

  23. L-14 Low Tokens Which Do Not Support Permit Validation Acknowledged
    Location
    BaseGelatoRelayRouter.sol: 252

    Description

    The _handleTokenPermits function does not validate that each target token supports the permit functionality.

    This lack of validation can lead to unexpected behaviors if the permit function is routed to a token contract's fallback function instead of the expected permit function, which does not exist on that token. No particularly malicious behaviors on popular tokens has been identified at this time.

    However with many tokens being supported on the GMX system and more to be added in the future, out of an abundance of caution it may be appropriate to validate that the tokens used with the _handleTokenPermits function are a whitelisted for permit use within GMX.

    Recommendation

    Consider adding validation to ensure that tokens used in the _handleTokenPermits function do indeed support permit functionality and are allowed by a whitelisted list.

    Resolution

    GMX Team: Acknowledged.

  24. L-15 Low Unnecessary Wnt Transfers To/From OrderVault Superfluous Code Resolved
    Location
    BaseGelatoRelayRouter.sol: 299

    Description

    The _handleRelayFee will handle Gelato relay fee payment, as well as GMX executionFee. However, when the fee token is wnt, it will perform unnecessary transfers from/to the orderVault:

    • transfer tokens from user to OrderVault (_handleRelayFee)
    • transfer out feeAmount from OrderVault (_swapFeeTokens)
    • transfer relay fee to collector (_transferRelayFee)

    Recommendation

    Do not transfer fee tokens from user to orderVault if the fee.feeToken = wnt and instead send them to the router contract. Then, avoid the call to _swapFeeTokens and only call _transferRelayFee as the tokens are already in the router.

    Resolution

    GMX Team: Resolved.

  25. L-16 Low Account Can Steal executionFee From subAccount Warning Acknowledged
    Location
    SubaccountRouter.sol

    Description

    An account can steal funds from the subaccount by manipulating the top up amount. The way this would be done is an account would signal to the sub account to create an order.

    Then the account will frontrun the sub account and decrease the top up amount, this will truncate the amount the sub account should be refunded to only a dust amount.

    Next the account will cancel the order where the execution fee will be sent to the cancelation receiver (account). If the subaccount is a bot or has any automation this attack would be easy to repeat stealing funds each time.

    Recommendation

    Consider putting a small time lock on setSubaccountAutoTopUpAmount and setMaxAllowedSubaccountActionCount

    Resolution

    GMX Team: Acknowledged.

  26. L-17 Low Account Can Avoid Refunding Subaccount Logical Error Acknowledged
    Location
    SubaccountRouter.sol: 218

    Description

    When an account does not have enough funds or allowance to repay the subaccount in the _autoTopUpSubaccount function it will return early. An account can leverage this by frontrunning a sub account and revoking allowance or transferring funds.

    Leading to the sub account not being compensated for paying the execution fee. An account can do this repeatedly to avoid all execution fee costs.

    Recommendation

    Instead of returning early either revert, or include an additional parameter where the sub account can choose if they want to waive the refund.

    Resolution

    GMX Team: Acknowledged.

  27. L-18 Low SubAccount Can Make Account Lose Funds Warning Acknowledged
    Location
    BaseGelatoRelayerRouter.sol

    Description

    The user can generate a sub account, the sub account action is managed by sub account router. However, in the newly added code, the sub account, if compromised, can directly make the main account lose money by executing a swap.

    The swap logic let the main account transfer the fee.feeToken and swap the fee token to output WETH token to pay for the relayer fee. The swap has minimum slippage control (minOutputAmount is _getFee()), so the main account can suffer from slippage loss when executing the swap.

    The sub account can also compose a long swap path market pass to force the main account pay high gelato relayer fee.

    Recommendation

    Document such attack vector if the sub account is compromised. Validate the fee.feeSwapPath and the amount of fee token (input token) the sub account can be used to swap for the WETH relayer fee.

    Resolution

    GMX Team: Acknowledged.

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