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

Security review · May 2023

GMX Key

for Key Finance

Key Finance engaged Guardian to review the security of its GMX rewards solution, GMX Key. From the 28th of March to the 10th of April, a team of 2 auditors reviewed the source code in scope. The auditing approach championed manual analysis to uncover novel exploits and verify intended behavior with ancillary verification from formal methods such as contract fuzzing. All findings and remediations have been recorded in the following report.

Published
Review window
March 28 to April 10, 2023
Language
Solidity
Chains
Arbitrum, Avalanche
Sector
Staking
  • 0 Critical
  • 3 High
  • 9 Medium
  • 5 Low
  • 0 Informational

14 resolved · 3 acknowledged

Scope

Overview

Key Finance engaged Guardian to review the security of its GMX rewards solution, GMX Key. From the 28th of March to the 10th of April, a team of 2 auditors reviewed the source code in scope. The auditing approach championed manual analysis to uncover novel exploits and verify intended behavior with ancillary verification from formal methods such as contract fuzzing. All findings and remediations have been recorded in the following report.

Issues Detected Throughout the course of the audit numerous high impact issues were uncovered and promptly remediated by the Key Finance team. Several issues impacted the fundamental behavior of the protocol, Guardian believes these issues to be resolved. However numerous changes were made to the codebase after Guardian’s review, for this reason Guardian supports an independent security audit of the protocol at a finalized frozen commit.

Findings 17

  1. LPS-1 High Rewards May Be Stolen Logical Error Resolved
    Location
    LPStaker.sol

    Description

    The uniswapV3Staker contract which the LPStaker interacts with allows any arbitrary address to directly call the unstakeToken function and unstake for any depositor after the incentive key endTime.

    When a deposit is unstaked from an incentive key directly from the uniswapV3Staker, those rewards will be incremented for the LPStaker contract, but not credited towards the user who staked. Therefore malicious stakers may unstake for other stakers and immediately claim their rewards as their own by unstaking through the LPStaker contract.

    Recommendation

    Consider using a modified version of the uniswapV3Staker where the depositor must always be the one to unstake. Otherwise be sure to manage the incentive keys extremely carefully and never allow an incentive key to reach its endTime while users have staked for it.

    Resolution

    Key Team: A modified version of the uniswapV3Staker was implemented.

  2. STK-1 High Reward Compounds Are Sandwichable Sandwich Attack Resolved
    Location
    Staker.sol

    Description

    There exists no fee or lockup period associated with staking to receive a portion of the rewards compounded during the updateAllRewardsForTransferReceiverAndTransferFee function.

    A malicious actor may simply buy GMXKey and stake right before the updateAllRewardsForTransferReceiverAndTransferFee function to immediately accrue a portion of the collected rewards that were meant to be attributed to other stakers. The malicious actor can then immediately claim these rewards, unstake and sell GMXKey after the reward compound, therefore stealing rewards from other stakers.

    Recommendation

    Consider implementing a staking/unstaking fee or a “warmup period” where stakers cannot accrue rewards.

    Resolution

    Key Team: A new approach to rewards including reward periods was adopted.

  3. TREC-1 High Lost WETH Rewards Upon Upgrade Lost Rewards Resolved
    Location
    TransferReceiver.sol

    Description

    During the account transfer process, the RewardRouterV2 does not claimForAccount from the feeGMXTracker. Therefore any accrued WETH rewards will not be transferred to the new address upon upgrade of the TransferReceiver.

    The TransferReceiver will then have no means of claiming these WETH rewards and injecting them into the Rewards system as the amountToMint calculation will underflow and revert since the receiver’s staked balances have been transferred out.

    Recommendation

    Require WETH rewards to be claimed and injected into the Rewards system before initiating the TransferReceiver upgrade process.

    Resolution

    Key Team: The Rewards logic was updated to allow any remaining WETH to be collected.

  4. GLOBAL-1 Medium Centralization Risk Centralization / Privilege Acknowledged
    Location
    Global

    Description

    The admin address holds the ability to negatively impact the system in numerous ways, including but not limited to:

    • Take all esGMX, sGMX and bnGMX via reserveSignalTransfer and signalTransfer.
    • Use the withdrawTokens function to take any non-WETH ERC20 rewarded to the TransferReceiver.
    • Lock all staked Uniswap V3 LP positions by pausing the LPStaker contract.
    • Lock all GMXKey and MPKey stakes by pausing the Staker contract.
    • Raise fees to 100% in the Rewards contract.

    Recommendation

    Ensure that the admin address is a multi-sig, optionally with a timelock for improved community trust and oversight. Attempt to limit the scope of the admin address permissions such as locking stakes and raising fees to 100%.

    Resolution

    Key Team: We have removed the pausable modifier for functions that would lock V3 positions and privileged addresses will be multi-sigs.

  5. TREC-2 Medium Invalid Assumption Logical Error Resolved
    Location
    TransferReceiver.sol

    Description

    In the acceptTransfer function it is assumed that A maximum of ~7% amount of GMX (as esGMX) would be added, however that assumption does not hold in several cases.

    A user could have removed their sGMX and only been left with esGMX and bnGMX or a user may have accepted a transfer from another account which perturbed this ratio.

    Recommendation

    Do not rely on this assumption holding and remove the comment. If it is paramount that only a small percentage of esGMX is added, add an explicit check.

    Resolution

    Key Team: The comment has been removed.

  6. TREC-3 Medium Unexpected Rewards Protocol Manipulation Resolved
    Location
    TransferReceiver.sol

    Description

    The allowance is used to determine how much WETH to inject into the Rewards system and it is incremented based on the current balance of the TransferReceiver.

    However the balance of the TransferReceiver can be inflated by transferring WETH directly to the TransferReceiver contract. Therefore rewards that are not explicitly from GMX are able to enter the Rewards system.

    Additionally, it is possible that privateTransferMode is turned off for either esGMX or bnGMX in the future, which could also potentially perturb the Rewards system.

    Recommendation

    Consider if outside WETH should be included in the accounted rewards. If not, implement a before and after balance check when calling rewardRouter.handleRewards to get the actual WETH amount received from GMX.

    Additionally, have a plan for the scenario where privateTransferMode is turned off for either esGMX or bnGMX.

    Resolution

    Key Team: The recommended before and after check was implemented.

  7. LPS-2 Medium onERC721Received Reentrancy Reentrancy Resolved
    Location
    LPStaker.sol

    Description

    During the unstakeAndWithdrawLpToken function, the msg.sender may re-enter into the onERC721Received function upon the withdrawToken call by transferring the withdrawn Uniswap V3 LP NFT back to the LPStaker.

    This reentrancy can yield an unexpected state where the token still exists in the tokensStaked list for the owner, but not in the idToOwner or stakedIndex. Such an unexpected state may have unintended consequences and effect frontend systems reading from the contract or third party systems built on top of the LPStaker.

    Recommendation

    Move the withdrawToken call to the end of the for loop to follow Check-Effects-Interactions. Alternatively, add a reentrancy check to the onERC721Received function.

    Resolution

    Key Team: Check-Effects-Interactions was adopted.

  8. STK-2 Medium Users May Stake For Others Unexpected Behavior Resolved
    Location
    Staker.sol

    Description

    The stake function in the Staker contract allows users to stake for any address rather than just their own. This can cause unexpected consequences for contract systems interfacing with the Staker contract, especially if the necessary staking “warmup period” is implemented.

    Recommendation

    Reconsider if this feature is necessary, and if so carefully document it and consider its impacts when combined with the solution for STK-1.

    Resolution

    Key Team: Users can no longer stake for others in the Staker contract.

  9. ADM-1 Medium Admin Role Changes Should Be Two Step Unnecessary Risk Resolved
    Location
    Adminable.sol

    Description

    As addressed in GLOBAL-1, the admin address carries numerous important abilities for the system.

    However the changeAdmin function allows the admin address to be errantly transferred to the wrong address as it does not use a two-step transfer process.

    Recommendation

    Implement a two step “push” and “pull” admin transfer process. If it is desired to have a method to relinquish ownership, implement a separate function to do so.

    Resolution

    Key Team: The recommended push and pull transfer process was adopted.

  10. BTOK-1 Medium Dangerous Approve Frontrunning Resolved
    Location
    BaseToken.sol

    Description

    The BaseToken only exposes the dangerous approve function rather than an additional alternative increaseAllowance function.

    Recommendation

    Implement an increaseAllowance function so that users may increase their allowances without risk of frontrunning.

    Resolution

    Key Team: The recommended increaseAllowance function was implemented.

  11. LPS-3 Medium Fee-On-Transfer Tokens Compatibility Acknowledged
    Location
    LPStaker.sol

    Description

    The LPStaker contract is not compatible with fee-on-transfer tokens for the rewardToken as it relies on the uint returned from the uniswapV3Staker claimReward function to increment the reward mapping.

    Fee on transfer tokens will cause this returned value to be inaccurate and potentially leave users unable to claim their rewards and potentially locked in the contract.

    It should also be noted that rebase tokens or other balance altering tokens will not be accurately accounted for in a similar way.

    Recommendation

    Consider if fee-on-transfer, rebase, or any similar tokens should be supported. If so, add before and after balance checks for the claimReward function to measure the reward claimed accurately.

    Resolution

    Key Team: The rewardToken will never be a fee-on-transfer token.

  12. REW-1 Medium Chain Incompatibility Compatibility Resolved
    Location
    Rewards.sol

    Description

    The withdrawTo function is used on WETH in the _transferAsETH function. This function is supported on Arbitrum, however it is not supported on the Avalanche C-chain or other networks that GMX may deploy on.

    Recommendation

    Do not use the withdrawTo function when deploying on Avalanche or other chains. Instead implement a method to receive Ether and relay it to the to address.

    Resolution

    Key Team: The withdrawTo function has been replaced with withdraw.

  13. GLOBAL-2 Low Custom Reverts Optimization Acknowledged
    Location
    Global

    Description

    Throughout the codebase require statements are used when instead custom errors may be implemented with if condition checks.

    Recommendation

    Replace require statements with if statements and custom error reverts to save gas.

    Resolution

    Key Team: Opted to keep the require statements.

  14. GLOBAL-3 Low Use Standard ReentrancyGuard Best Practices Resolved
    Location
    Global

    Description

    Throughout the codebase a non OpenZeppelin ReentrancyGuard contract is used. The custom ReentrancyGuard contract is inferior as it uses a boolean _guard storage variable.

    Recommendation

    Use the OpenZeppelin ReentrancyGuard.

    Resolution

    Key Team: Implemented the recommended OZ ReentrancyGuard.

  15. REW-2 Low Redundant Transfers Optimization Resolved
    Location
    Rewards.sol: 187

    Description

    When claiming and updating a reward for a TransferReceiver, the fee is first transferred to the TransferReceiver before being transferred to the msg.sender.

    Recommendation

    Consider implementing a feeTo address parameter on the updateAllRewardsForTransferReceiverAndTransferFee function so that two redundant transfers are not needed.

    Resolution

    Key Team: The suggested feeTo address was implemented.

  16. CNV-1 Low Inaccurate Comment Documentation Resolved
    Location
    Converter.sol: 177, 210

    Description

    In the completeConversion and completeConversionToMpKey functions it is stated that the sender's vesting tokens must be non-zero however the sender’s vesting tokens must be zero or else the acceptTransfer on the RewardRouter will revert.

    Recommendation

    Update the inaccurate comments.

    Resolution

    Key Team: The comment was updated.

  17. GLOBAL-4 Low Lack of Events Events Resolved
    Location
    Global

    Description

    Throughout the codebase there are functions that alter the contract state in a significant way without emitting an event.

    For example the signalTransfer and reserveSignalTransfer ought to emit an event for third party systems to be able to read.

    Recommendation

    Emit an appropriate event whenever a significant change is made in the contract system.

    Resolution

    Key Team: The suggested events were added.

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