GMX engaged Guardian to review the security of its decentralized synthetics perpetuals exchange. From the 18th of April to the 15th of May, a team of 4 auditors reviewed the source code in scope.
- Published
- Review window
- April 18 to May 15, 2023
- Language
- Solidity
- Chains
- Arbitrum, Avalanche
- Sector
- Perpetuals
- 3 Critical
- 9 High
- 14 Medium
- 17 Low
- 0 Informational
Scope
Overview
GMX engaged Guardian to review the security of its decentralized synthetics perpetuals exchange. From the 18th of April to the 15th of May, a team of 4 auditors reviewed the source code in scope.
Findings 43
-
CON-1 Critical Missing Keys In Config Configuration Resolved
Description
Several critical keys are missing from the
initAllowedBaseKeysfunction:MIN_POSITION_SIZE_USDMAX_PNL_FACTOR_FOR_DEPOSITSMAX_PNL_FACTOR_FOR_ADL
Recommendation
Add the missing keys to
initAllowedBaseKeys.Resolution
GMX Team: The recommendation was implemented in commit 7bbadf29.
-
DPCU-1 Critical Unliquidatable Position Due To getLiquidationValues Logical Error Resolved
Description
Proof of concept: PoC
In the
getLiquidationValuesfunction thevalues.pnlAmountForPoolis reset to a new value, although the previous value may have been used in a swap from thepnlTokento thecollateralToken.This causes mis-accounting in the market and causes a revert with the market token balance check upon liquidation. Therefore positions can be unliquidatable, yielding a potentially catastrophic amount of bad-debt for the market.
Recommendation
The solution is to account for the previous
values.pnlAmountForPoolin the event that this swap was made. e.g. add:if (wasSwapped) { MarketUtils.applyDeltaToPoolAmount( params.contracts.dataStore, params.contracts.eventEmitter, params.market.marketToken, values.pnlTokenForPool, values.pnlAmountForPool ); }to the
elsebranch ingetLiquidationValues.Resolution
GMX Team: The recommendation was implemented in commit cd9f9c2.
-
DPCU-2 Critical Mis-Accounting When Swap Fails Logical Error Resolved
Description
Positions in profit with unpaid borrowing/funding fees that are greater than the position’s collateral open the exchange up to several high-impact issues when the
swapProfitToCollateralTokenswap fails. These positions are able to exist since theisPositionLiquidatablecheck factors positive PnL as collateral that would purportedly always be able to cover these fees.However
swapProfitToCollateralTokenwill commonly fail whenever thevalidatePoolAmount,validateReserve, orvalidateMaxPnlchecks fail as a result of the swap, causing the following issues:- Positions in large profit would be un-ADL-able since the ADL order would revert on line 224 as the collateral is not sufficient to cover the fees alone.
- Liquidations for these positions result in the user losing all of their profit since the execution enters
getLiquidationValues. - Liquidations for these positions result in the protocol having to cover a potentially large deficit between the position’s collateral and the unpaid funding fees.
- The pool value for market depositors sees a stepwise jump down from the potentially large unpaid borrowing fees.
- Decrease orders expecting to be able to use their profit to pay fees will be cancelled/frozen
Recommendation
Do not allow positions to exist when their fees are greater than the actual collateral backing the position. Allow positions to be liquidated when their fees negate the collateral backing amount.
Resolution
GMX Team: The team acknowledged this scenario and made the unswapped PnL claimable in commit c1d9428.
-
MKTU-1 High Malicious Actor Can Break Markets Underflow Resolved
Description
Proof of concept: PoC
The
uint poolValueis decreased by the impact pool value before the PnL is added to thepoolValue. Additionally, the impact pool value is not capped to avoid underflow.Because of this, there are some cases where the market can be entirely bricked when the value of the impact pool surpasses the value of the backing tokens — even if it was meant to offset a positive pool PnL.
A malicious actor can engineer this outcome in certain scenarios, especially when the pool is initially deployed.
Recommendation
Consider moving the
poolValue -= result.impactPoolAmount * indexTokenPrice.pickPrice(maximize);line to after the PnL is added to thepoolValue, as theimpactPoolAmountis meant to offset initial positive pnl. Otherwise, consider making thepoolValueanintwithin thegetPoolValueInfofunction.Additionally, consider capping the value of the impact pool (similarly to the capping of PnL) that is subtracted from the
poolValueto avoid any cases where the market is bricked.Resolution
GMX Team: The recommendation was implemented in commit aea23c6.
-
MKTU-2 High Unclaimable Funding Fees Logical Error Resolved
Description
In the
getExpectedMinTokenBalancefunction, thecollateralForLongsandcollateralForShortsis included in the resultingexpectedMinBalance.Therefore users who are paid out funding fees will be unable to claim them until the users who are paying the funding fees update their position. There is no requirement for users to frequently update their position and therefore funding fees can go a long time without being claimable for users.
Recommendation
Adjust
getExpectedMinTokenBalancesuch that funding fees that will be paid from user’s collateral can be immediately claimed without affecting the validation.Resolution
GMX Team: The recommendation was implemented in commit 5b1be2f.
-
TIME-1 High Wrong Key For Signer Removal Logical Error Resolved
Description
When a remove oracle signer is signaled, the action key used is:
bytes32 actionKey = _removeOracleSignerActionKey(account);However,
removeOracleSignerAfterSignaluses an incorrect action key to validate the signal. Specifically, it uses_addOracleSignerActionKeyinstead of_removeOracleSignerActionKey.Therefore a signer cannot be removed using the
removeOracleSignerAfterSignalfunction.Recommendation
Use the
_removeOracleSignerActionKeyin theremoveOracleSignerAfterSignalfunction.Resolution
GMX Team: The recommendation was implemented in commit 3ef1161.
-
BOU-1 High Tight Stop Loss Abuse Logical Error Resolved
Description
For stop-losses, the
triggerPriceis used to represent the price of the asset instead of just using it as a trigger for execution. In traditional markets, the stop-loss price is not the guaranteed execution price especially in times of heavy volatility.Users can open a long and place a SL ever so slightly below the current price. If they get stopped out then they will lose out on fees. However, with high leverage, the upside gain is immense with little risk. Such a high reward will come at the expense of the pool, hurting LPers and the market as a whole.
Ultimately, this allows sophisticated traders to have a superior strategy than that of traditional stop loss orders where they are treated like mere triggers – hurting the profitability of LPers.
Recommendation
Use the latest price (
secondaryPrice) when executing a stop loss order rather than giving the user their exacttriggerPrice.Resolution
GMX Team: Order pricing was switched to validate against primary price in commit 3243138.
-
ERTR-1 High UI Fee Manipulation Protocol Manipulation Resolved
Description
The
uiFeecan be manipulated during the order/deposit/withdrawal execution to determine whether or not the action is executed and circumvent thevalidateRequestCancellationperiod.Ultimately this allows malicious users to make a short-term risk-free trade as they can decide whether or not their action should be executed successfully with prices from a few blocks ago.
For example, during a withdrawal, a malicious user can re-enter the system during the execution of the first swap (
WithdrawalUtils.sol: 388) into theExchangeRouter.setUiFeeFactorfunction and change theuiFeeFactorfor theuiFeeReceiverof the withdrawal executed.The change in
uiFeeFactorcan make the difference between the subsequent swap satisfying theminOutputAmount— therefore deciding whether the withdrawal can go through.Additionally, note that a similar effect can be achieved for any order/deposit/withdrawal by simply front-running the execution tx and changing the
uiFee.Notice that malicious
uiFeeReceiverscan manipulate theuiFeeFactorafter a user submits their order/deposit/withdrawal. This way auiFeeReceivercan promise auiFeeof .05%, but adjust it to be much higher right before the actual execution.Recommendation
Do not allow the
uiFeeFactorthat is experienced in the execution to be changed after the order/deposit/withdrawal is created/updated.Resolution
GMX Team: Acknowledged.
-
ORDU-1 High Referral Code Manipulation Protocol Manipulation Resolved
Description
A malicious user can manipulate the referral code associated with their account to decide whether or not a trade should be executed.
For example, the referral code could be switched to a higher discount just in time to allow a
MarketIncreaseexecution tx to pass theorder.minOutputAmount()validation and go through.This allows a malicious trader to make a short-term risk-free trade as they can decide whether or not they should be executed successfully with prices from a few blocks ago.
Additionally, the
customDiscountShareof a single discount code could be leveraged in the same way.Notice that allowing the referral codes to be adjusted after orders are submitted also allows for referrer manipulations. This way referrers can promise a 2% discount but front-run order executions to adjust the
customDiscountSharesuch that the trader receives no discount.Recommendation
Store the referral discount on a per-order basis. Do not allow the referral discount to be adjusted in real-time by either the referral code being used or the
customDiscountShareof a particular code.Resolution
GMX Team: Acknowledged.
-
BOU-2 High Stop-loss Won’t Execute On Price Gap Logical Error Resolved
Description
The prices for a stop-loss are required to straddle the trigger price in
setExactOrderPrice. In the case of a price gap where both the primary and secondary prices fall below/above the trigger price, the stop-loss will fail to execute. This will prevent a position’s profit from being secured or loss to be mitigatedFor example, if the trigger price is $100 for a long SL but price gaps to $99 (primary) -> $98 (secondary) the SL will not be triggered and the user will still have exposure in the market.
Recommendation
Do not revert if both primary and secondary prices fall below/above the trigger price.
Resolution
GMX Team: Validation is now performed only between primary and trigger price in commit 3243138.
-
DPCU-3 High Unliquidatable Position Due to PriceImpactDiff Logical Error Resolved
Description
Proof of concept: PoC
When a position is liquidated with
getLiquidationValuesthepnlAmountForPoolis set toparams.position.collateralAmount() - fees.funding.fundingFeeAmount.However, this value does not account for the amount incremented for the claimable collateral with
incrementClaimableCollateralAmountin the event that the price impact is capped.This will result in a revert since the
claimableCollateralAmountis included in thegetExpectedMinTokenBalance. Therefore making a position unliquidatable when the price impact is capped.Recommendation
Be sure to appropriately set aside the
collateralCache.pnlDiffAmountin thecache.pnlTokenwhen liquidations enter thegetLiquidationValuesfunction.Resolution
GMX Team:
pnlAmountForPooldelta is applied to the pool prior to the swap which includes thepnlDiffAmountin commit cd9f9c2. -
BOU-3 High Position Impact Pool Manipulation Protocol Manipulation Resolved
Description
Proof of concept: PoC
When calculating the
PositionPricingUtils.getPriceImpactAmountthe difference between theexecutionPriceand thelatestPriceis used to derive the resultingpriceImpactAmountfor thepositionImpactPool.However, the
executionPricecan be modified to be theacceptablePricein the event that theacceptablePricecannot be fulfilled by the initial max/minlatestPrice. This will lead to a difference in theexecutionPriceand thelatestPricethat is not necessarily from thepriceImpactUsdamount.Example
- Consider a
LimitIncreaseLong order priceImpactUsdis 0 for simplicity, although this applies whenpriceImpactUsdis nonzero- The
acceptablePriceis not fulfilled by thetriggerPrice(max) so theacceptablePriceis used triggerPriceis used as the_latestPriceingetPriceImpactAmount- However
triggerPrice != acceptablePrice(where theacceptablePriceis myexecutionPrice) - Therefore there is a nonzero
priceDiffingetPriceImpactAmount, this is errantly credited as positive PI and taken out of the impact pool when there is no impact.
As a result, when the
acceptablePriceis more favorable than thetriggerPrice, thepositionImpactPoolis decreased even when the user caused a non-trivial imbalance in the OI and initially had negativepriceImpactUsd.This will influence the
positionImpactPoolto trend towards 0 as more orders that have anacceptablePricethat is more favorable than thetriggerPriceare executed. Ultimately this stifles any amount of positive impact that can be offered to users to balance the OI, since the positive impact amount is capped to the balance of thepositionImpactPool.Recommendation
Consider removing the feature where the user may get their acceptable price if the first price + impact is not fulfillable and rather revert and have the order canceled/frozen if the
acceptablePriceis not met.Otherwise do not allow users to set an
acceptablePricethat is more favorable than thetriggerPrice.Resolution
GMX Team: The recommendation was implemented in commit 3243138.
- Consider a
-
MKTU-3 Medium Pending Borrowing Fees Brick Withdrawals Underflow Resolved
Description
Proof of concept: PoC
It is possible for user’s withdrawals to revert because the pending borrowing fees are attributed to the user’s withdrawal but have not yet been added to the
poolAmount.In cases where there is a significant amount of unpaid borrowing fees this can become a non-trivial issue for users attempting to withdraw.
Recommendation
Consider allowing a separate claiming process for borrowing fees, or implementing a pathway for regular position updates to pay the pending borrowing fees.
Resolution
GMX Team: Acknowledged.
-
SWPU-1 Medium Max Price Used For Swap Pricing Logical Error Resolved
Description
The
getLatestPricefunction is used to get prices for swaps, however, this will return the custom price for the token if any is set. In the case of aMarketIncreaselong order, the min and max price for thecustomPriceare both the max of theprimaryPrice. The inverse can be true using aMarketDecreaseshort order.This way users can get more favorable execution while swapping for the index token during a Market order.
This invalidates the implemented protection where the
inTokenis supposedly valued at the minimum price and theoutTokenis valued at the max price:cache.amountOut = cache.amountIn * cache.tokenInPrice.min / cache.tokenOutPrice.max;Recommendation
Do not allow the max of the
primaryPriceto be used as the price for thetokenInduring a swap.Resolution
GMX Team: The recommendation was implemented in commit 3243138.
-
SWOU-1 Medium LimitSwaps Unnecessarily Delayed Logical Error Resolved
Description
The
validateOracleBlockNumbersfunction forLimitSwapsdoes not allow oracle block numbers to be equal to theorderUpdatedAtBlock. This is in contradiction to the oracle block validation for increase and decrease orders.Additionally, this unnecessarily requires that limit swaps be executed at a delayed block number, when the current block number may provide a more favorable execution for the trader.
Recommendation
Change the requirement from
!minOracleBlockNumbers.areGreaterThan(orderUpdatedAtBlock) to!minOracleBlockNumbers.areGreaterThanOrEqualTo(orderUpdatedAtBlock).Resolution
GMX Team: The recommendation was implemented in commit c5fdc29.
-
DOU-1 Medium Minimum Output Amount Griefing Griefing Resolved
Description
A malicious actor can observe a user’s
triggerPricefor their stop-loss (among other order types) approaching and shift price impact in the user’s market (or in the user’s virtual inventory) such that their minimum output becomes invalidated and the order gets canceled.In some cases this could cause significant grief to users who would have otherwise exited the market. A malicious actor may stand to benefit from this by holding MarketTokens and griefing traders within that market.
Recommendation
Document this behavior clearly to users. Monitor such manipulations and disincentivize them accordingly by adjusting the price impact factors as necessary.
Resolution
GMX Team: Acknowledged.
-
KEY-1 Medium Wrong Key For Pool Adjustment Typo Resolved
Description
The
poolAmountAdjustmentKeyfunction inKeys.soluses thePOOL_AMOUNTkey instead of thePOOL_AMOUNT_ADJUSTMENTkey.There are luckily no catastrophic consequences as this is an
intvalue and thepoolAmountKeyis auint, however, it poses a significant risk to any future changes and would cause confusion/potential issues for those reading using thePOOL_AMOUNT_ADJUSTMENTkey.Recommendation
Alter the
poolAmountAdjustmentKeyfunction to use thePOOL_AMOUNT_ADJUSTMENTkey.Resolution
GMX Team: The key was entirely removed.
-
IPU-1 Medium Price Impact Double Counted Double Counting Resolved
Description
When increasing a position,
PositionUtils.validatePositionis called after incrementing the OI for the position increase. However, the validation will re-compute the price impact amount based on this updated OI.The resulting
cache.priceImpactUsdinisPositionLiquidatablewould be inaccurate to the actual price impact experienced. Therefore some positions may be errantly prevented from being opened with this validation.Recommendation
Validate the position based on the previous OI, therefore accurately representing the OI delta of the order.
Resolution
GMX Team: Acknowleged.
-
DPU-1 Medium Token Amount Added To USD Value Logical Error Resolved
Description
The
estimatedRemainingCollateralUsdis incremented by a token amount rather than a token amount multiplied by a price:estimatedRemainingCollateralUsd += params.order.initialCollateralDeltaAmount().toInt256();
As a result, the main effect is
estimatedRemainingCollateralUsdis much smaller than it should be and the position is more likely to get closed out in its entirety unexpectedly due to theMIN_COLLATERAL_USDcheck.Recommendation
Multiply the
params.order.initialCollateralDeltaAmount()by the price of the collateral token to receive a USD value before adding it with theestimatedRemainingCollateralUsd.Resolution
GMX Team: The recommendation was implemented in commit f8f2dd6.
-
EDPU-1 Medium Users Are Negatively Affected By The Price Spread Logical Error Resolved
Description
When the
positiveImpactAmountis calculated during a deposit, the_params.priceImpactUsdis divided by thetokenPrice.maxto be converted into anoutTokenamount. However the token amount is converted back to a USD amount when incrementing themintAmount,positiveImpactAmount.toUint256() * _params.tokenOutPrice.min.This means that users are negatively impacted by the price spread because they receive less positive impact than they otherwise would have.
Recommendation
Multiply the
positiveImpactAmountby the_params.tokenOutPrice.maxso that users are not negatively impacted by the price spread.Resolution
GMX Team: The recommendation was implemented in commit 27a9164.
-
GLOBAL-1 Medium Liquidations When Features Disabled Logical Error Resolved
Description
It is possible for a state to arise where liquidations are enabled but other order types are disabled. For example, increase orders can be disabled and a user is unable to add collateral to their position. Fees will accumulate until a user’s position is liquidatable which leads to loss of funds.
Recommendation
Consider disallowing liquidations when a user is unable to adjust their order due to a feature being disabled.
Resolution
GMX Team: Acknowledged.
-
OCL-1 Medium Lack Of Sequencer Uptime Check Validation Resolved
Description
Even with the heartbeat validation, it may be prudent to validate that the Sequencer is active to prevent stale pricing.
This would avoid any scenarios where price movement triggers an update within the heartbeat duration but the new price is not reported to the L2, allowing traders to take advantage of stale pricing.
Recommendation
Validate whether the Sequencer is active or not. Furthermore, document keeper behavior in the case that the Sequencer is down.
Resolution
GMX Team: Acknowledged.
-
CHAIN-1 Medium Hardcoded Chain ID Configuration Resolved
Description
In the
Chain.solfile,uint256 constant public ARBITRUM_CHAIN_ID = 42161;is hardcoded.From a comment in
Oracle.sol, the codebase wishes to be impervious to a change in the chain ID:// it might be possible for the block.chainid to change due to a fork or similarHowever in the event that the Arbitrum chain ID changes, the
currentBlockNumberandgetBlockHashfunctions will not return the appropriate Arbitrum values, potentially causing drastic effects on the exchange.Recommendation
Add a configurable chain ID for Arbitrum.
Resolution
GMX Team: Acknowledged.
-
POSU-1 Medium Negative PnL Ignored In Sufficient Collateral Check Validation Resolved
Description
The PnL of the remaining position is no longer accounted for during the
willPositionCollateralBeSufficientcheck.In the case where the remaining position PnL is positive, this avoids errantly counting profit towards the remaining position’s collateral.
However, in the case where the remaining position PnL is negative, this check fails to consider that the remaining PnL could make the actual value backing the position significantly smaller than the
minCollateralUsdForLeverage.It may be prudent to consider the PnL of the remaining position, only when it is in a loss. This way the negative PnL, which would be subtracted from the collateral in the position, is taken into account.
Recommendation
Consider factoring the remaining position’s PnL into the
willPositionCollateralBeSufficientcheck, only when the remaining PnL is negative and would be subtracted from the collateral in any future order.Resolution
GMX Team: Acknowledged.
-
GLOBAL-2 Medium Block Re-org Attack Block Re-org Resolved
Description
In the event of a block re-org a malicious trader may see that price has moved against them and decide to get their order canceled rather than recorded.
Consider the following scenario:
- Bob sees the keeper execute his
MarketIncreasein block A - The block re-org occurs over a period of 1 minute
- Bob sees the re-org is happening and sees that the execution of his order has since lost money and gets his tx to cancel the order recorded in block B which will come before block A.
- Bob can make his tx cancel his
MarketIncreaseby having it send the tokens necessary for theMarketIncreaseorder elsewhere. - Bob’s order is canceled rather than executed since it did not net him any profit.
Notice that there are likely many ways to exploit the two-step execution process during a re-org.
Recommendation
Beware of potential risks to the system during block re-orgs and communicate that risk with users. Consider implementing a mechanism to freeze all orders that were executed during a block re-org.
Resolution
GMX Team: Acknowledged.
- Bob sees the keeper execute his
-
GLOBAL-3 Medium Multiple Read-only Reentrencies Reentrancy Resolved
Description
There are several instances where a user may gain control over the order execution tx before the
dataStorehas been properly updated.Firstly, in the
SwapOrderUtils.swapfunction, theSwapUtils.swapis executed before the swap order is removed from thedataStore. Since the swap can haveshouldUnwrapNativeToken == truetheorder.receiverwill get called upon receiving the output of the swap.Since native token transfers forward 200,000 gas (from the test environment) the receiver will have ample gas to potentially exploit any system building on top of GMX V2 and relying on the swap order in the
dataStore.Similarly in the
OrderUtils.cancelOrderfunction, theorderVault.transferOutis executed before the order is removed from thedataStore.Recommendation
In the
SwapOrderUtils.swapfunction, remove the order withOrderStoreUtils.removebefore theSwapUtils.swap. And in theOrderUtils.cancelOrderfunction, remove the order withOrderStoreUtils.removebefore theorderVault.transferOut.This way third parties building on top of GMX V2 cannot be exploited by the outdated order state in these instances.
Resolution
GMX Team: The recommendation was implemented in commit 27a9164.
-
GLOBAL-4 Low Minimum Order Size Validation Resolved
Description
Similarly to the minimum position size, it may be prudent to add a minimum order size for both the
sizeDeltaUsdandinitialCollateralDeltaAmountin the case where they are greater than 0 to avoid potential manipulation.Recommendation
Consider introducing a minimum
sizeDeltaUsdand a minimuminitialCollateralDeltaAmountthat take effect when either of each field is not 0.Additionally, it may be prudent to introduce similar minimums for deposits and withdrawals.
Resolution
GMX Team: Acknowledged.
-
BOU-4 Low Superfluous positionKey Variable Superfluous Code Resolved
Description
The
ExecuteOrderParamsstruct contains apositionKeyvariable that is never assigned nor referenced.Recommendation
Remove the
positionKeyvariable from theExecuteOrderParamsstruct.Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-5 Low Revert Reason Unnecessarily Parsed Optimization Resolved
Description
In the
_handleOrderError,_handleWithdrawalErrorand_handleDepositErrorfunctions, the revert reason is parsed before it is necessary. In many cases the function will return before the parsedstring memory reasonis used.Recommendation
Move the
ErrorUtils.getRevertMessagecall after theErrorUtils.revertWithCustomErrorcase to save gas in the event of a revert.Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-6 Low validateMarketTokenBalance After External Call Validation Resolved
Description
Throughout the codebase it is mentioned that the internal state changes of a market should be validated with
validateMarketTokenBalancebefore handing over execution of the tx to a user.However there are several places where the user may gain control of the tx before
validateMarketTokenBalancehas been called.If a user has
shouldUnwrapNativeToken == true, they will gain control over execution of the tx before the internal state changes of a market have been validated upon the swaps in withdrawals and orders.Recommendation
Consider additionally validating the internal state changes of the market before potentially handing over control of the tx execution to an arbitrary address on swaps.
Resolution
GMX Team: Acknowledged.
-
ERR-1 Low Superfluous Error Superfluous Code Resolved
Description
The
InvalidFactorerror is never used.Recommendation
Remove the
InvalidFactorerror.Resolution
GMX Team: The recommendation was implemented.
-
EDPU-2 Low Recomputed Value Optimization Resolved
Description
The
cache.longTokenAmount * prices.longTokenPrice.midPriceis re-computed when calculating price impact during a deposit but this value is already stored in thecache.longTokenUsd.Similarly for
cache.shortTokenUsd.Recommendation
Use
cache.longTokenUsdandcache.shortTokenUsdrather than re-computing these values.Resolution
GMX Team: The recommendation was implemented.
-
PPU-1 Low Outdated NatSpec Documentation Resolved
Description
The NatSpec documentation for the
GetPriceImpactUsdParamsstruct refers to alongTokenandshortTokenthat are no longer there.Recommendation
Update the NatSpec documentation for the
GetPriceImpactUsdParamsstruct.Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-7 Low ERC-777 Tokens Warning Resolved
Description
Although the exchange does not intend to use ERC-777 tokens it should be emphasized that the system is vulnerable to them. Tokens with callbacks allow receivers to revert with an arbitrary revert string that can potentially cause a revert in the decoding process leading to a risk free trade opportunity.
Recommendation
Take care with the tokens able to be used on the exchange. Do not ever allow ERC-777 tokens to be used in the system.
Resolution
GMX Team: Acknowledged.
-
PPU-2 Low Misleading Comment Documentation Resolved
Description
The comment purports that the
usdDeltaoffset is necessary to prevent overflow, however it prevents underflow.Recommendation
Update the comment to mention underflow instead of overflow.
Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-8 Low Invalid Market Risk Warning Resolved
Description
If there exists a single market in the GMX ecosystem that fails the
validateMarketTokenBalancecheck on a swap or for any other reason, it can be leveraged to perform a short term risk free trade.A malicious user can include one of these markets in their
swapPathand decide whether the order should be able to go through by “plugging the hole” in the market that would otherwise fail thevalidateMarketTokenBalancecheck.Recommendation
Monitor markets closely to observe if any have entered such a state and be careful to not introduce any such markets with admin intervention.
Additionally, consider using the
validateMarketTokenBalancecheck on the markets involved in a users order, deposit, or withdrawal upon creation.Resolution
GMX Team: Acknowledged.
-
OCL-2 Low Misleading Comment Documentation Resolved
Description
The comment in the
getLatestPricefunction mentions that theacceptablePricemay be used as thecustomPricebut this is not true.Recommendation
Do not mention the
acceptablePricein this comment.Resolution
GMX Team: The recommendation was implemented.
-
MKTU-4 Low Prefer applyFactor Precision Resolved
Description
In the
getNextTotalBorrowingfunction theprevPositionBorrowingFactorandnextPositionBorrowingFactorare applied without the use ofapplyFactor.Recommendation
Favor the use of
applyFactorfor theprevPositionBorrowingFactorandnextPositionBorrowingFactor.Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-9 Low Superfluous Code Superfluous Code Resolved
Description
The
revertOracleBlockNumbersAreNotEqualfunction is never utilized and therefore theErrors.OracleBlockNumbersAreNotEqualerror is never thrown.Therefore the entire
ifstatement checking forerrorSelector ==Errors.OracleBlockNumbersAreNotEqual.selectorin theisOracleBlockNumberErrorfunction can be removed.Recommendation
Remove the unused error and related logic.
Resolution
GMX Team: The recommendation was implemented.
-
ORDU-2 Low Duplicate Validation Superfluous Code Resolved
Description
An order is validated to be non-empty twice during the
createOrderfunction. Theifcase on line 130 can be removed as it is duplicated by thevalidateNonEmptyOrdercall.Recommendation
Remove the bespoke
ifcase.Resolution
GMX Team: The recommendation was implemented.
-
KEY-2 Low Outdated NatSpec Documentation Resolved
Description
There are two
claimableFundingAmountKeyfunctions, one with anaccountaddress parameter and one without. However the NatSpec for both of them references anaccountaddress parameter.Recommendation
Remove the
accountparameter in the NatSpec for theclaimableFundingAmountKeywhich does not have such a parameter.Resolution
GMX Team: The recommendation was implemented.
-
DS-1 Low Errant Import Superfluous Code Resolved
Description
The
Printer.solfile is errantly imported into theDataStore.solfile.Recommendation
Remove the unnecessary import.
Resolution
GMX Team: Acknowledged.
-
OCL-3 Low Direct Use Of block.timestamp Consistency Resolved
Description
Throughout the codebase
Chain.currentTimestampis utilized however in the_getPriceFeedPricefunctionblock.timestampis used directly.Recommendation
Use
Chain.currentTimestamprather thanblock.timestampdirectly.Resolution
GMX Team: The recommendation was implemented.
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.
