GMX engaged Guardian to review the security of its liquidity migration contracts from GLP to GMX V2 Markets. From the 9th of October to the 23th of October, a team of 2 auditors reviewed the source code in scope.
- Published
- Review window
- October 9 to 23, 2023
- Language
- Solidity
- Chains
- Arbitrum, Avalanche
- Sector
- Perpetuals
- 0 Critical
- 2 High
- 6 Medium
- 2 Low
- 0 Informational
Scope
Overview
GMX engaged Guardian to review the security of its liquidity migration contracts from GLP to GMX V2 Markets. From the 9th of October to the 23th of October, a team of 2 auditors reviewed the source code in scope.
Findings 10
-
GLPM-1 High Reduced Redemption Fees Gamed Logical Error Acknowledged
Description
In the
_redeemGlpfunction, users are allowed to make any arbitrary external calls with theredemptionInfo.externalCallTargetsandredemptionInfo.externalCallDataList.Therefore a user seeking to redeem GLP using the discounted redemption may do so as the external call is executed within the context of the
withReducedRedemptionFeesmodifier.Users may abuse the system in a similar way by simply providing an EOA address as the
redemptionInfo.receiverrather than theDepositVault, or by using the subsequent external call to transfer out the redeemed tokens to their EOA.Recommendation
Require that the
redeemedTokenAmount(or at least a majority, accounting for potential fees & slippage) end up in theDepositVaultcontract.This validation also serves as a safety net in the event that the provided
redemptionInfo.externalCallTargetsorredemptionInfo.externalCallDataListhold errors.Resolution
GMX Team: Acknowledged and comment added in commit 2de90ca.
-
GLPM-2 High USDC vs USDC.e Validation Resolved
Description
Currently GLP consists of a large amount of USDC.e (bridged USDC), which has a different token address than USDC. On the other hand, GMX V2 pools all use USDC.
Because a user is required to pass a
migrationItem.short.tokenthat matches thecache.market.shortToken, they cannot redeem with USDC.e astokenOutsince the short token validation will revert.Rather, the user is forced to redeem for the limited amount of USDC directly so that they can deposit the USDC into the GMX V2 market.
Recommendation
In the case of USDC, modify the
InvalidShortTokenForMigrationcheck such that USDC.e can still pass and then be swapped for native USDC.Furthermore, consider adding a state check after the external calls to check the token balances of the
depositVault, to ensure the user has not mistakenly sent the USDC.e to the GMX V2 system and lost it.Resolution
GMX Team: The
InvalidLongTokenForMigrationandInvalidShortTokenForMigrationchecks have been removed in commit 3a696d5. -
GLPM-3 Medium Additional Ether Lost Logical Error Resolved
Description
If the provided
msg.valueis greater than theexecutionFee * migrationItems.lengththen the excess Ether is not refunded to the user and can be used by the user who calls the migrate function next.Recommendation
Add validation to ensure that
msg.value == executionFee * migrationItems.length, otherwise refund any excess Ether to the caller.Resolution
GMX Team: The recommendation has been implemented in commit 58312d4.
-
GLPM-4 Medium User Forced To Deposit Both Tokens Logical Error Resolved
Description
In
RewardRouter.solfor GMX V1, functionunstakeAndRedeemGlp()requires that_glpAmount > 0:require(_glpAmount > 0, "RewardRouter: invalid _glpAmount");In the GlpMigrator contract, if a user wants to only redeem for a the long token in a market, and leaves the
GlpRedemption shortwithmigrationItem.short.glpAmount = 0, the migration will fail due to the above revert.This is unexpected behavior as it forces users to not only populate the
migrationItem.short.tokento match the market's short token, but also set a miniscule amount ofglpAmountto redeem for the short token to avoid migration failure.The same behavior applies if a user wants to solely redeem for the short token in a market, and leave the long token untouched.
Recommendation
If the
glpAmountfor either the long or short token is 0, skip the callunstakeAndRedeemGlp()for that token.Resolution
GMX Team: The recommendation has been implemented in commit 58312d4.
-
GLPM-5 Medium Reduced Burn Fee Can Be Larger Than Current Validation Resolved
Description
GMX is choosing to reduce the burn fee to further incentivize migration from GMX V1 to its latest GMX V2 system. However, there is no guarantee that the
reducedMintBurnFeeBasisPointsis less than or equal to the currentmintBurnFeeBasisPoints.The burn fee can be increased to be larger than its current value, causing users to redeem less tokens than expected.
Recommendation
Inside modifier
withReducedRedemptionFees, only update the burn fee in GMX V1 if the_reducedMintBurnFeeBasisPointsis smaller:bool shouldUpdateFees = _reducedMintBurnFeeBasisPoints < mintBurnFeeBasisPoints;Resolution
GMX Team: The recommendation has been implemented in commit 58312d4.
-
GLPM-6 Medium Lists With Different Lengths Validation Resolved
Description
The
externalCallTargetsandexternalCallDataListare used to call an external protocol with user-passed data.However,
externalCallDataListmay have a different length thanexternalCallTargets, potentially causing an out-of-bounds error or the incorrect data being used for a particular target.Similarly,
refundTokensandrefundReceiversmay be different lengths, potentially causing an out-of-bounds error or funds being sent to an unintended receiver.Recommendation
Add validation such that
externalCallTargetsandexternalCallDataListare the same length and thatrefundTokensandrefundReceiversare the same length:require(externalCallTargets.length == externalCallDataList.length)require(refundTokens.length == refundReceivers.length)Resolution
GMX Team: The recommendation has been implemented in commit 3a696d5.
-
EXTH-1 Medium Lack of Contract Existence Check Low-Level Calls Resolved
Description
The low-level
callreturns a success boolean oftrueif the target contract does not exist. As a result, the migration may not detect some failed external calls, leading to loss of funds for users.Recommendation
Consider implementing a contract existence check prior to the
call.Resolution
GMX Team: The recommendation has been implemented in commit 3a696d5.
-
EXTH-2 Medium Lack Of safeTransfer For Arbitrary Token Logical Error Resolved
Description
In the
makeExternalCallsfunction, the arbitraryrefundTokenis transferred using thetransferfunction, howeversafeTransfershould be used to avoid potential loss if the token chooses to returnfalserather than reverting upon failure.Recommendation
Prefer
safeTransfertotransfer.Resolution
GMX Team: The recommendation has been implemented in commit 3a696d5.
-
GLPM-7 Low Inflexible executionFee Optimization Resolved
Description
The same
executionFeeis used for everymigrationItemin themigrationItemslist, however some migrations may require a smallerexecutionFeethan others depending on if they are single token deposits.Recommendation
Allow an individual
executionFeeto be specified on amigrationItembasis.Resolution
GMX Team: The recommendation has been implemented in commit 58312d4.
-
GLPM-8 Low Migration Contracts Needs To Be Set As Handler Access Control Resolved
Description
In order for
glpTimelock.setSwapFees()to succeed, the GlpMigrator contract must be given the necessary access control to bypass theonlyKeeperAndAbovemodifier in GMX V1.Recommendation
Set the migration contract as a handler in the GLP Timelock contract.
Resolution
GMX Team: Confirmed the
GlpMigratorwill have the necessary privileges.
No findings match.
More from GMX
All 44 reportsPut 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.
