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

Security review · September 2023

Chainlink Automation

for GMX

GMX engaged Guardian to review the security of the Chainlink real time pricing automation system for GMX V2. From the 4th of September to the 18th of September, a team of 3 auditors reviewed the source code in scope.

Published
Review window
September 4 to 18, 2023
Language
Solidity
Chains
Arbitrum, Avalanche
Sector
Infrastructure
  • 2 Critical
  • 1 High
  • 2 Medium
  • 8 Low
  • 0 Informational

8 resolved · 5 acknowledged

Scope

Overview

GMX engaged Guardian to review the security of the Chainlink real time pricing automation system for GMX V2. From the 4th of September to the 18th of September, a team of 3 auditors reviewed the source code in scope.

Findings 13

  1. GLOBAL-1 Critical Orders Requiring More Than 5 Million Gas Cannot Execute Insufficient Gas Resolved
    Location
    Global

    Description

    The Chainlink automation keepers are configured to send 5,000,000 gas with each execution, however GMX deposits, withdrawals, and orders may often require more than 5,000,000 gas to execute.

    In the general.ts configuration file the increaseOrderGasLimit and decreaseOrderGasLimit are configured to 4_000_000, additionally the minAdditionalGasForExecution is configured to 1_000_000. Therefore any increase or decrease order including a callback with a nonzero callbackGasLimit cannot be executed by the Chainlink automation keepers.

    Additionally the singleSwapGasLimit is configured to 1_000_000, therefore deposits and withdrawals with 4 or more total swaps in the longTokenSwapPath and shortTokenSwapPath are unable to be executed by the Chainlink automation keepers.

    Recommendation

    Increase the configured gas amount to as high as 15,000,000 to ensure that even the most expensive of actions can be executed on GMX V2.

    Resolution

    Chainlink Team: The configuration was adjusted to 13,000,000 gas after discussing with the GMX team.

  2. WTDA-1 Critical Withdrawals With Swaps Are Incompatible Logical Error Resolved
    Location
    WithdrawalAutomation.sol: 66

    Description

    The longTokenSwapPath and the shortTokenSwapPath of a withdrawal cannot be decoded. This is because WithdrawalEventUtils.emitWithdrawalCreated does not emit the swap paths on a Withdrawal.

    Consequently, the withdrawal automation becomes unusable whenever a user requires a swap post-withdrawal.

    Recommendation

    Add the longTokenSwapPath and shortTokenSwapPath to the WithdrawalCreated event. Afterwards, modify WithdrawalAutomation to decode these paths and _addPropsToMapping to set the necessary feeds.

    Resolution

    GMX Team: The necessary event data was added in commit 3122a9.

    Chainlink Team: The longTokenSwapPath and shortTokenSwapPath logic was added to the WithdrawalAutomation in commit fc35f03.

  3. GLOBAL-2 High Anyone May performUpkeep Access Control Resolved
    Location
    Global

    Description

    There is no access control for the performUpkeep function, therefore any arbitrary address may execute an order. On a network with a public mempool, such as Avalanche, any arbitrary user may observe the Chainlink keeper’s transaction and copy the performData to execute their own deposit, withdrawal, or order.

    A malicious actor can therefore front-run the execution of other user’s orders to manipulate price impact such that the actor stands to gain a profit at the user’s detriment.

    Recommendation

    Add access controls to the performUpkeep function in the MarketAutomation, DepositAutomation, and WithdrawalAutomation contracts.

    Once access controls are added to the performUpkeep function, it should be noted that the permissioned caller still holds the ability to decide execution ordering, controlling the price impact experienced by orders.

    Resolution

    Chainlink Team: The suggested onlyForwarder access control was added in commit 0a7edc2.

  4. MKTA-1 Medium performUpkeep Can Be Used To Execute Any orderType Validation Acknowledged
    Location
    MarketAutomation.sol: 128

    Description

    Using the performUpkeep function, the Chainlink keeper may execute any order, even if the order is not a MarketIncrease, MarketDecrease, or MarketSwap.

    However the MarketAutomation contract is explicitly designed to execute only market orders, therefore the scope of which orders can be executed with the performUpkeep function should be limited by validating the orderType from the dataStore.

    Recommendation

    Validate that the order being executed is indeed a MarketIncrease, MarketDecrease, or MarketSwap.

    Resolution

    Chainlink Team: Acknowledged.

  5. GLOBAL-3 Medium FeedIds Must Be Set For Every Token Configuration Resolved
    Location
    Global

    Description

    Deposits, withdrawals and orders may use swap paths including any token supported on the GMX V2 platform.

    Therefore every supported token must be assigned a valid functioning feedId, otherwise deposits, withdrawals and orders may be unable to be executed using the Chainlink keeper or may become cancelled unexpectedly.

    Recommendation

    Ensure that every token on the GMX V2 platform is supported by a valid feedId.

    Additionally, implement validation in the checkLog function such that if a feedId is not configured for a token that is necessary for the action, the action is not executed by the Chainlink keeper and is instead executed by the default keeper.

    Resolution

    Chainlink Team: The recommended validation was added in commit a21b0b9.

  6. MKTA-2 Low Typo Typo Resolved
    Location
    MarketAutomation.sol: 51

    Description

    The comment on line 51 reads a feed lookup lookup where the word lookup is repeated twice.

    Recommendation

    Remove the second instance of lookup.

    Resolution

    Chainlink Team: The recommendation was implemented in commit ffed912.

  7. GLOBAL-4 Low Accept Ether Optimization Optimization Acknowledged
    Location
    Global

    Description

    The DepositAutomation, MarketAutomation, and WithdrawalAutomation contracts have no receive function and therefore cannot receive gas remuneration from GMX V2 in native tokens.

    GMX V2 handles this by re-wrapping the native tokens and sending them to the keeper. However the keeper must pay for this additional re-wrapping gas expenditure on every transaction.

    Recommendation

    To avoid this additional gas expenditure on every transaction, implement a receive function, with a way to retrieve the accumulated native tokens.

    Resolution

    Chainlink Team: Acknowledged.

  8. GLOBAL-5 Low Lack Of cannotExecute Modifier For checkLog Validation Acknowledged
    Location
    Global

    Description

    The ILogAutomation contract documentation mentions that a cannotExecute modifier should be added to the checkLog function to ensure it can never be errantly called in a transaction, only simulated.

    However the checkLog functions in the DepositAutomation, MarketAutomation, and WithdrawalAutomation contracts lack any cannotExecute or similar modifier.

    Recommendation

    Consider whether a cannotExecute modifier should be added.

    If so, implement a cannotExecute modifier in the GMXAutomationBase contract and add it to the checkLog functions in the DepositAutomation, MarketAutomation, and WithdrawalAutomation contracts.

    Resolution

    Chainlink Team: Acknowledged.

  9. GLOBAL-6 Low Inaccurate Comment Documentation Resolved
    Location
    Global

    Description

    In the DepositAutomation and WithdrawalAutomation contracts the comment on line 53 states // Decode Event Log 1, however both "DepositCreated" and "WithdrawalCreated" are emitted with Event Log 2.

    Recommendation

    Update the comment to // Decode Event Log 2.

    Resolution

    Chainlink Team: The recommendation was implemented in commit ffed912.

  10. GLOBAL-7 Low Key Contract Addresses May Not Be Updated Upgradeability Acknowledged
    Location
    Global

    Description

    In the DepositAutomation, WithdrawalAutomation, and MarketAutomation contracts the i_depositHandler, i_withdrawalHandler, and i_orderHandler variables are declared as immutable. Additionally, in the GMXAutomationBase contract the i_dataStore and i_reader variables are declared immutable.

    However it is possible that the respective contracts are upgraded or replaced, meaning that the existing Chainlink Automation contract is no longer functional. Therefore a new Automation contract would need to be deployed and whitelisted as a valid keeper in the event of a handler contract upgrade.

    Recommendation

    Consider if the owner address should be able to update these contract addresses in the event of an upgrade.

    Resolution

    Chainlink Team: Redeployment of an Automation contract is preferred on the Chainlink operational side.

  11. GLOBAL-8 Low Block Number Provided As Time Documentation Acknowledged
    Location
    Global

    Description

    The StreamsLookup error labels the second to last parameter as time, it is unclear whether this is intended to be a timestamp.

    However a block number is provided for the time in the StreamsLookup error in the checkLog function in each of the DepositAutomation, WithdrawalAutomation, and MarketAutomation contracts.

    Recommendation

    Verify whether the time parameter is intended to be a block number or timestamp, consider documenting what the time parameter represents in the StreamsLookupCompatibleInterface interface.

    Resolution

    Chainlink Team: This is expected.

  12. BASE-1 Low Missing NatSpec Documentation Resolved
    Location
    GMXAutomationBase.sol: 81

    Description

    In the NatSpec for the _flushMapping function, the addresses returned value is omitted.

    Recommendation

    Add the addresses value to the NatSpec for the _flushMapping function.

    Resolution

    Chainlink Team: The recommendation was implemented in commit ffed912.

  13. BASE-2 Low Typo Typo Resolved
    Location
    GMXAutomationBase.sol: 101

    Description

    In the _toHexString function, the comment on line 101 states // Fixed buffer size for hexadecimal convertion, however conversion is misspelled as convertion.

    Recommendation

    Replace convertion with conversion.

    Resolution

    Chainlink Team: The recommendation was implemented in commit ffed912.

Invariants 1

The review's fuzzing suite asserted 1 invariant. 1 held.

Every invariant tested
IDInvariantResult
BASE-1_toHexString Matches OpenZeppelin’s toHexString output with a length of 32 bytesHeld

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