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

Security review · December 2023

Governance

for GMX

GMX engaged Guardian to review the security of its RewardRouterV2 and Governance updates. From the 5th of December to the 12th of December a team of 3 auditors reviewed the source code in scope.

Published
Review window
December 5 to 12, 2023
Language
Solidity
Chains
Arbitrum, Avalanche
Sector
Governance
  • 0 Critical
  • 1 High
  • 3 Medium
  • 5 Low
  • 0 Informational

5 resolved · 4 acknowledged

Scope

Overview

GMX engaged Guardian to review the security of its RewardRouterV2 and Governance updates. From the 5th of December to the 12th of December a team of 3 auditors reviewed the source code in scope.

Findings 9

  1. RROU-1 High Burning bnGMX Could Be Avoided Logical Error Acknowledged
    Location
    RewardRouterV2.sol: 505

    Description

    Proof of concept: PoC

    When unstaking GMX and _shouldReduceBnGmx = true, a proportionate amount of the user's bnGMX wallet balance is burnt.

    A user can avoid the loss of their bnGMX (any amount beyond what is claimed in _unstakeGmx) by transferring the token to another account they control.

    After unstaking, the sent bnGMX could be returned. Consequently, a user will have more multiplier points than intended, which will lead to more voting power.

    Recommendation

    Consider restricting the transfer of bnGMX for non-handler accounts. Furthermore, clearly document the intended behavior regarding bnGMX slashing upon unstaking GMX.

    Resolution

    GMX Team: bnGMX.inPrivateTransferMode is not currently set to true, agree that it should be set to true.

  2. GLOBAL-1 Medium Payload Attack Enables Griefing Of Keepers Griefing Resolved
    Location
    Global

    Description

    The _transferOutETHWithGasLimitFallbackToWeth and _transferOutETH functions in GMX V1 are used to transfer ether.

    Neither of these functions utilizes assembly to avoid copying return data into memory.

    This allows any contract interacting with GMX V1 to maliciously return a large amount of data, which will end up costing more gas than anticipated and could force the keepers to run a deficit.

    Recommendation

    Use a low-level call to avoid loading the return data into memory: assembly { success := call(gasLimit, receiver, amount, 0, 0, 0, 0) }.

    Resolution

    GMX Team: The resolution was implemented.

  3. GLOBAL-2 Medium Incorrect block.timestamp Used Logical Error Resolved
    Location
    Global

    Description

    In the GovToken and ProtocolGovernance contracts, the CLOCK_MODE function validates that the clock function returns the block.timestamp, however it should be validated against the Chain.currentTimestamp to avoid failures due to L2 timestamp drift.

    Recommendation

    Validate the result of the clock function against the Chain.currentTimestamp().

    Resolution

    GMX Team: The resolution was implemented.

  4. RROU-2 Medium Infinite Voting Power Logical Error Acknowledged
    Location
    RewardRouterV2.sol: 537

    Description

    Proof of concept: PoC

    When syncing an account's voting power, the user's staked amounts are compared against their current governance token wallet holdings. If the governance holdings are less, the appropriate amount is minted.

    This can be exploited if a user transfers their governance tokens to another account, triggers a sync, and then those governance tokens are minted again. This way, a user can generate infinite votes and move forward malicious proposals.

    Recommendation

    Ensure that the governance token used cannot be freely transferred.

    Resolution

    GMX Team: GovToken.sol would be used, only contracts with the role GOV_TOKEN_CONTROLLER would be able to make transfers.

  5. RROU-3 Low Unnecessary Vote Syncing In _compound Optimization Acknowledged
    Location
    RewardRouterV2.sol: 343

    Description

    In the acceptTransfer function, the _compound invocation will always trigger a syncing of the voting power for the _sender. However, the voting is once again synced after stake balances are transferred at the end of the acceptTransfer function.

    Recommendation

    Consider adding a boolean parameter to the _compound function to specify whether the voting power should be synced. In the case of an account transfer this boolean would be false to avoid unnecessary syncs to save gas. In all other cases the boolean value would be true.

    Resolution

    GMX Team: Acknowledged.

  6. RROU-4 Low Early Return Misses Voting Sync Logical Error Resolved
    Location
    RewardRouterV2.sol: 481

    Description

    In the _stakeBnGmx function the _syncVotingPower invocation will be missed if the currentBnGmxAmount is greater than the maxAllowedBnGmxAmount.

    There is no net effect as the _syncVotingPower function will always be called for the _account later for every instance where the _stakeBnGmx function is used.

    However, this poses a risk if the _stakeBnGmx function were to be used without syncing the voting power of the _account afterwards.

    Recommendation

    Consider removing the _syncVotingPower invocation from the _stakeBnGmx function as it is redundant for all cases where the _stakeBnGmx function is currently used.

    Otherwise be sure to sync the voting power in the case where the currentBnGmxAmount is greater than the maxAllowedBnGmxAmount and the _stakeBnGmx function early returns.

    Resolution

    GMX Team: The recommendation was implemented.

  7. RROU-5 Low Unnecessary Voting Sync In _stakeGmx Optimization Resolved
    Location
    RewardRouterV2.sol: 446

    Description

    In the _stakeGmx function it is unnecessary to sync the voting power for the funding account as the funding account will provide unstaked tokens, while the voting power relies on staked tokens.

    Therefore the voting power of the funding account cannot be affected by the _stakeGmx function.

    Additionally, in the only case where the _stakeGmx function is used with a _fundingAccount that is different from the account is in the acceptTransfer function where the voting power is redundantly synced for the _sender at the end.

    Recommendation

    Remove the syncing logic for the _fundingAccount in the _stakeGmx function as it is unnecessary.

    Resolution

    GMX Team: The recommendation was implemented.

  8. RROU-6 Low inStrictTransferMode Does Not Ensure Successful acceptTransfer Logic Error Acknowledged
    Location
    RewardRouterV2.sol: 325

    Description

    Calling the function signalTransfer requires that msg.sender has given the _receiver allowance equivalent to or more than the balance of msg.sender when inStrictTransferMode is set to true.

    The original msg.sender can decrease the approval prior to the receiver accepting the transfer, causing the function acceptTransfer to revert in the case where the transfer is not being performed by a handler.

    When the transfer is performed by the handler, transferFrom does not check the allowance on the BaseToken.

    Recommendation

    Consider removing the inStrictTransferMode check since the transfers are being done by handlers, or clearly document its intended behavior.

    Resolution

    GMX Team: The reason for inStrictTransferMode is mainly to prevent phishing, we have observed phishing scams that utilize signalTransfer, because no approval is needed, there is less warning / less alerting of the user from their wallet

  9. DEPLOY-1 Low Miniscule proposalThreshold Configured Logical Error Resolved
    Location
    deployProtocolGovernor.ts: 15

    Description

    In the deploy file for the ProtocolGovernor contract, 30_000 is used as a proposalThreshold. However, this is a minuscule amount of governance tokens, as the governance token is configured with 18 decimals.

    Recommendation

    Be sure to configure a reasonable minimum threshold with 18 decimals for proposals in production as the proposalThreshold is in a governance token amount, which has 18 decimals of precision.

    Resolution

    GMX Team: The recommendation was implemented.

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