GMX engaged Guardian to review the security of its decentralized synthetics perpetuals exchange. From the 31st of January to the 15th of March, a team of 4 auditors reviewed the source code in scope.
- Published
- Review window
- January 31 to March 15, 2023
- Language
- Solidity
- Chains
- Arbitrum, Avalanche
- Sector
- Perpetuals
- 16 Critical
- 9 High
- 31 Medium
- 41 Low
- 0 Informational
Scope
Test suiteGuardianAudits/GMX-6
Overview
GMX engaged Guardian to review the security of its decentralized synthetics perpetuals exchange. From the 31st of January to the 15th of March, a team of 4 auditors reviewed the source code in scope.
Findings 97
-
GLOBAL-1 Critical Homogeneous Markets Double Count Value Double Counting Resolved
Description
Proof of concept: PoC
When using the
getPoolValuefunction for markets with identical long and short backing tokens, thecache.longTokenAmountandcache.shortTokenAmountrepresent the same token amount.However both of these token amounts are counted in the value of the pool. Therefore the deposited pool value is doubled.
Additionally, several validations e.g.
validateReserve,validateMaxPnl, andvalidatePoolAmountamong others are immediately invalidated as they errantly count the same exact token amount for both the short and long side. This way the single backing token pool is effectively double counted as backing both long positions and short positions.Similarly open interest in these markets is double counted in the
getNextFundingAmountPerSizefunction, producing a completely invalid calculation for funding fees.Recommendation
Reconsider if markets with the same backing
longTokenandshortTokenshould be possible. If they should, then handle their accounting/validation separately.Resolution
GMX Team: A pool divisor has been added to account for these markets.
-
ORDU-1 Critical Unbounded swapPath Length Gas Manipulation Resolved
Description
Proof of concept: PoC
When creating an order there is no validation that the
swapPathis under a certain max length. This allows malicious users to create risk-free trades on the exchange.Notably, among other ways, a trader may submit a
MarketIncreaseorder with aswapPaththat puts the order just over the block gas limit when combined with a callback that consumes nearly the entiremaxCallbackGasLimit. When a trader wishes the trade to be executed using outdated prices, they can toggle the callback contract to only consume a very small amount of gas, enabling the order to be executed and recorded in a block.Recommendation
Add validation on the max length for the
swapPathof orders to protect the exchange from the entire class ofswapPathgas manipulation attacks.Resolution
GMX Team: A maximum
swapPathlength has been implemented. -
PPU-1 Critical Open Interest Value Uninitialized Logical Error Resolved
Description
Proof of concept: PoC
In the
getNextOpenInterestParamsfunction thenextLongOpenInterestandnextShortOpenInterestvariables are not initialized from the default values and only one is set in either of theparams.isLongcases.This drastically misrepresents the open interest balance of the market and yields nonsensical price impact calculations.
Recommendation
Initialize each of the
nextLongOpenInterestandnextShortOpenInterestvalues to the current open interest on each side.Resolution
GMX Team: The recommendation was implemented.
-
MKTU-1 Critical Impact Pool Included In Pool Value Logical Error Resolved
Description
Proof of concept: PoC
In the
getPoolValuefunction, the position impact pool value is added to the value of the pool. However the position impact pool is comprised of a notional value of synthetic index tokens, therefore it should not be added to the pool value.When a trader is negatively impacted, two things happen:
indexTokensare taken from the trader and allocated to the position impact pool.- The trader immediately experiences a loss on their PnL.
This results in the pool accounting for this negative impact amount twice. Once for the
indexTokensthat are allocated to the position impact pool. And a second time for the negative PnL that the trader has just experienced.This double counting invalidates the market’s accounting system and does not allow depositors to withdraw all of their
MarketTokens.Recommendation
Do not account the position impact pool as a part of the net
poolValue.Resolution
GMX Team: The value of the impact pool is now subtracted from the
poolValue. -
MKTU-2 Critical Funding Fees Partially Paid Logical Error Resolved
Description
Proof of concept: PoC
In the
getNextFundingAmountPerSizefunction, thefundingAmountPerSizePortionvalues are calculated by dividing the correspondingfundingUsdby the open interest of both longs and shorts. This is done to get a value that is to be received/paid for each collateral type for each position direction.This logic makes sense in the case where users are being paid the funding fees since they are able to claim both the long and short tokens. However users are only able to pay the
fundingAmountPerSizePortionvalue for their respective collateral token.This errantly reduces the amount the
fundingAmountPerSizePortionthat is paid for each corresponding collateral token as the amounts are divided by the entire open interest of the side that is paying funding fees. As a result, only a portion of the funding fees are being paid while the entire amount can be claimed, and a deficit in the pool accounting is created which will force markets into insolvency over time.Recommendation
Implement separate logic for the side that is paying the funding fees where the funding fees are not spread across the entirety of the open interest for that side, but rather the open interest of that side that is able to pay the particular token through their collateral.
E.g.
cache.fps.fundingAmountPerSizePortion_LongCollateral_LongPosition =getPerSizeValue(cache.fundingUsdForLongCollateral / prices.longTokenPrice.max,cache.oi.longOpenInterestWithLongCollateral);Resolution
GMX Team: The funding fees are now divided amongst traders with the appropriate collateral. 23
-
MKTU-3 Critical Unclaimable Collateral Logical Error Resolved
Description
Proof of concept: PoC
When users attempt to claim their collateral, the
adjustedClaimableAmountis asserted to be<theclaimedAmount, otherwise the tx reverts. However if a user has not claimed any of their collateral the claimed amount will be 0 and therefore theadjustedClaimableAmountcannot be strictly less than theclaimedAmount.Therefore users are unable to claim their collateral.
Recommendation
Modify the
ifstatement to revert in the case whereadjustedClaimableAmount < claimedAmount.Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-2 Critical Blacklisted Addresses Can Exploit The Exchange Blacklisted Addresses Resolved
Description
Proof of concept: PoC
Addresses that are blacklisted for popular ERC20 tokens such as USDC can be leveraged to exploit the exchange in a number of ways.
These addresses cannot be liquidated in any case where they would be transferred back a leftover collateral amount in a token which they are blacklisted for.
Among other ways, blacklisted addresses can execute risk-free trades using
MarketIncreaseorders in the following way:- Force the collateral swap to fail via low liquidity in a niche market.
- The order cannot be cancelled since the cancellation would attempt to send the token that the user is blacklisted for.
- Therefore the order will remain in the
dataStoreuntil the liquidity is added. - Deposit liquidity into the low liquidity market so the
MarketIncreasecan go through when the attacker wants it to, using out of date prices for a risk-free trade.
Recommendation
Be extremely cautious when adding markets with tokens that include a blacklist. Consider implementing checks to see if users are blacklisted and denying them service to the relevant markets.
Resolution
GMX Team: If a user is found to be blacklisted when transferring to them, the tokens are now sent to a holding address.
-
DPCU-1 Critical Wrong Token Amount Applied Logical Error Resolved
Description
Proof of concept: PoC
In the case where the
remainingCollateralfor a position is negative during a liquidation, thegetLiquidationValuesfunction is called. Thevalues.pnlTokenForPoolis used in the returned values. However thevalues.pnlAmountForPoolcomputed on line 347 is strictly a collateral token amount.A position may be in profit and still be liquidated due to the
minCollateralUsdForLeveragecombined with a depreciation in the user’s collateral token price.Therefore the
pnlTokenForPoolmay be different from the collateral token, in which case ashortTokenamount could be applied as alongTokenamount or vice-versa. This will drastically perturb thepoolAmountfor thepnlTokenForPoolin such a way that could leave the market insolvent or simply cause extreme loss for the market depositors.Recommendation
Adjust the
getLiquidationValuesfunction so that it accounts for the cases where traders are being liquidated in profit.Resolution
GMX Team: The
getLiquidationValuesfunction was refactored. -
CBU-1 Critical Malicious Revert Bytes Gas Manipulation Resolved
Description
Proof of concept: PoC
In each callback function the
bytes memory reasonBytesreturned from the third party callback contract in the event of an error is loaded into memory in thecatchcase.reasonBytescan be potentially very large and therefore extremely gas intensive when it is copied into memory.Furthermore, the catch block continues to perform computation with
reasonBytes, first parsing the error message withErrorUtils.getRevertMessageand also emitting an event that containsreasonBytes.An attacker may simply implement a callback contract that reverts with an extremely large
reasonBytesso that the execution tx would require more gas than the block gas limit. The attacker can then toggle the callback contract to no longer revert when they want their order to be executed successfully, enabling a risk-free trade.In another attack, a trader could observe the keeper’s execution tx in the mempool and front-run it to toggle the callback contract to revert with a large
reasonBytes. This would cause the keeper’s execution tx to consume an unforeseen amount of gas and likely run out of gas and fail. The attacker could leverage this in a similar manner to create a risk-free trade opportunity.Recommendation
Do not load the third party callback contract’s error
reasonBytesinto memory.Resolution
GMX Team: The recommendation was implemented.
-
DPCU-2 Critical LimitDecrease Gamed With EmptyPosition Error Protocol Manipulation Resolved
Description
Proof of concept: PoC
Removing all of the collateral from a position will result in the
EmptyPositionerror, which will be retried forLimitDecreaseorders.An attacker can leverage this by creating a
LimitDecreaseorder that initially only reduces theirposition.sizeInUsdby half, but reduces their collateral to 0. TheLimitDecreasewill continue to result in theEmptyPositionerror until the attacker creates aMarketDecreaseorder that reduces the size of their position by half. Now when the originalLimitDecreaseis executed, it will close the position and no longer revert with theEmptyPositionerror.A malicious trader can leverage this to make a risk-free trade with their
LimitDecrease.Recommendation
Revert with the
InsufficientCollateralerror, which is not retried, in the case wherevalues.remainingCollateralAmountis 0.Resolution
GMX Team: Limit orders no longer revert on the
EmptyPositionerror. -
MKTU-4 Critical 60 Decimals Of Precision Causes Overflow Overflow Resolved
Description
Proof of concept: PoC
In the
getPoolValuefunction, thecache.totalBorrowingFeesutilizes 60 decimals of precision, thereforePrecision.applyFactor(cache.totalBorrowingFees, cache.borrowingFeeReceiverFactor)will have 60 decimals of precision.Whenever an LP wants to withdraw after borrowing fees have been accumulated, the call to
marketTokenAmountToUsdinWithdrawalUtils.solwould overflow since the pool value is multiplied by the market token amount which has 18 decimals of precision.Furthermore, the amount of market tokens depositors receive will be drastically reduced as the pool value is inflated.
Recommendation
Do not use 60 decimals of precision for the
cache.totalBorrowingFeesor account for this additional precision when applying the factor.Resolution
GMX Team: The precision handling was refactored.
-
GSU-1 Critical Missing Swap Gas Estimation Gas Attack Resolved
Description
Proof of concept: PoC
The
estimateExecuteWithdrawalGasLimitdoes not account for either thelongTokenSwapPathor theshortTokenSwapPath, therefore the keeper will not be remunerated for users using the swap feature on withdrawals.This will lead to the protocol keeper being unexpectedly drained of the native token, potentially stopping execution on the exchange for a period of time. The gas draining can occur due to regular exchange usage or can be easily leveraged by an attacker to maliciously drain the keeper.
Recommendation
Add gas estimation logic for the
longTokenSwapPathandshortTokenSwapPathin theestimateExecuteWithdrawalGasLimitfunction, similar to the estimation for deposits.Resolution
GMX Team: The recommendation was implemented.
-
IPU-1 Critical Rounding Leads To Risk Free Trade Protocol Manipulation Resolved
Description
Proof of concept: PoC
In
increasePosition, thesizeDeltaInTokensis rounded down for long positions. An attacker can provide asizeDeltaUsdthat is 1 wei less than theirtriggerPriceand have theirsizeDeltaInTokensrounded to 0. In this case aLimitIncreasewould revert with theEmptyPositionerror.The attacker can make a
LimitIncreaselong for a niche market with low open interest where it is easy to manipulate the price impact by controlling the open interest. The attacker can then manipulate the open interest such that theirLimitIncreaselong is positively price impacted, meaning theirexecutionPriceis decreased. A reduction in theexecutionPricewould cause thesizeDeltaInTokensto no longer be rounded to 0, and the order to no longer revert with anEmptyPositionerror.The attacker can leverage this with a large
initialCollateralDeltaAmountand aswapPaththat allows them to take advantage of outdated prices for a risk-free trade.Recommendation
Do not allow users to create position orders with any
sizeDeltaUsdless than a particular value such as 1e30 e.g. $1.Resolution
GMX Team: Limit orders no longer revert on the
EmptyPositionerror, additionally a minimum position size was implemented. -
CON-1 Critical _validateRange Prevents Critical Values Being Set Logical Error Resolved
Description
Proof of concept: PoC
The
_validateRangefunction does not perform any validation on the value, but rather reverts for specific keys such as theKeys.SWAP_FEE_FACTORandKeys.POSITION_FEE_FACTOR.This restricts the protocol from setting these crucial values for the exchange.
Recommendation
Update the
_validateRangefunction to perform validation on the value being passed rather than simply reverting for certain keys.Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-3 Critical Referral Codes Used To Game Orders Protocol Manipulation Resolved
Description
Proof of concept: PoC
A trader is able to update their
referralCodeat any time by creating a new order through theExchangeRouter.A malicious trader can submit a
LimitIncreasewhere thefees.totalNetCostAmountwill be their exactinitialCollateralDeltaAmount, yielding anEmptyPositionerror on execution.The malicious trader can then allow the
LimitIncreaseto be executed once they see that prices have moved in their favor by updating theirreferralCodeso that theirfees.totalNetCostAmountis discounted and now the order will no longer revert with theEmptyPositionerror.This attack can also be executed if the
tierortraderDiscountFactoris updated on a trader’s currentreferralCodeas well.Recommendation
Do not allow a trader’s
totalRebateFactorortraderDiscountFactorto be updated in any way for existing orders.Resolution
GMX Team: Limit orders no longer revert on the EmptyPosition error.
-
GLOBAL-4 Critical positionIncreasedAtBlock Used To Game Orders Protocol Manipulation Resolved
Description
Proof of concept: PoC
LimitIncrease,LimitDecrease, andStopLossDecreaseorders depend on thepositionIncreasedAtBlockfor the price ranges that they can be executed with.A malicious trader may take advantage of past prices by closing their position to reset the
positionIncreasedAtBlockto 0.Consider the following attack:
- A trader makes two
LimitIncreaseorders with the sametriggerPrice. - The first one is successfully executed and the
positionIncreasedAtBlockis set to 100. - The second one is not executed since the valid descending price range is from before the
positionIncreasedAtBlock. - The trader waits and observes that price moves in their favor, and then closes their position with a
MarketDecreaseorder. Therefore resetting thepositionIncreasedAtBlockto 0. - Now the second
LimitIncreaseis able to be executed with the out of date price range, enabling a risk-free trade.
Recommendation
Track when the position was closed and add this block number to the validation for
LimitIncrease,LimitDecrease, andStopLossDecreaseorders so that they cannot be used to abuse past prices.Resolution
GMX Team: The
positionIncreasedAtBlockvalidation was removed forLimitIncreaseorders, this gaming should not apply to decrease orders. - A trader makes two
-
MKTU-5 High Fee Receiver Amount Included in Pool Value Logical Error Resolved
Description
Proof of concept: PoC
The pool value is incremented by
Precision.applyFactor(cache.totalBorrowingFees,cache.borrowingFeeReceiverFactor)which is the portion of borrowing fees going to thefeeReceiver.Because this amount is paid to the
feeReceiverrather than the pool, it should not be counted as part of the pool value. This misrepresents the pool’s accounting and incorrectly values the pool for deposits and withdrawals.Recommendation
Include the portion of pending borrowing fees which will go into the pool rather than the portion allocated for the
feeReceiver.Resolution
GMX Team: The recommendation was implemented.
-
MKTU-6 High Rounding Error Causes Market Insolvency Precision Loss Resolved
Description
Proof of concept: PoC
When the per-size values are computed, for tokens such as USDC with only 6 decimals of precision and large open interest values, there can be rounding error due to the division that occurs in
toFactor.For example:
- $10,000,000 of open interest will have a total number of 38 digits.
- 1 USDC will have a total number of 37 digits after multiplying by
FLOAT_PRECISION. - Therefore when calculating the per-size values, precision loss on the order of <10 USDC can occur due to truncation.
Additionally, when calculating the per-size values, the resulting values are often not exact multiples of each other.
For example:
- Open Interest is $10,000 short and $20,000 long both with the long token as collateral.
- Assume
cache.fundingUsdForShortCollateral / prices.shortTokenPrice.maxyields57600399999999999. 57600399999999999 / 10,000 = 5760039999999will be the short funding factor magnitude.57600399999999999 / 20,000 = 2880019999999will be the long funding factor.- However
2880019999999 / 5760039999999 = ~0.49999999999991 != 0.5.
This can result in less funding fees being paid than collected. Such a deficit will eat into the balance of the market and potentially prevent LPers from withdrawing their entire deposits or prevent traders from claiming their funding fees.
Recommendation
Avoid this precision loss for low precision tokens so that the same amount of funding fees are paid as collected. Address the precision lost when the resulting per-size values are not exact multiples.
-
PPU-2 High Price Impact For Trader != Price Impact For Pool Accounting Error Resolved
Description
Proof of concept: PoC
As shown in the following calculations, the price impact experienced by a trader differs from the amount taken out of/into the impact pool which over time will lead to meaningful accounting inconsistencies in the pool.
Recommendation
Consider using
int256 priceImpactUsd = size.toInt256() * priceDiff / executionPrice.toInt256()instead ofint256 priceImpactUsd = size.toInt256() * priceDiff / _latestPrice.toInt256()on line 182.Resolution
GMX Team: The recommendation was implemented.
-
DPU-1 High Outdated Fees For Liquidation Check Logical Error Resolved
Description
Proof of concept: PoC
The
isPositionLiquidatablevalidation is checked beforePositionUtils.updateFundingAndBorrowingState(params, cache.prices)is performed.Therefore the borrowing/funding fees are potentially significantly outdated when being accounted for.
This leads to the liquidation keeper not being able to liquidate positions that would be liquidateable when accounting for borrowing/funding fees and ultimately exposes the market to bad debt once the fees are updated.
Recommendation
Update the funding and borrowing state before checking whether the position is liquidatable.
Resolution
GMX Team: The recommendation was implemented.
-
ORDH-1 High Unaccounted Gas Expenditure When Setting Prices Gas Attack Resolved
Description
Proof of concept: PoC
The
startingGasvariable is declared inside of theexecuteOrderfunction. As a result, it will be the amount of gas left after the setting of prices, which is particularly gas intensive.When calculating how much gas was used in
GasUtils.payExecutionFee, keepers won't be remunerated for this expenditure which will run a significant deficit over time.Recommendation
Refactor the way gas expenditure is tracked so that the gas used for the
withOraclePricesmodifier is included in the keeper’s remuneration.Resolution
GMX Team: This gas expenditure should be accounted for in the base fee.
-
EDPU-1 High Adjusting Long and Short Token Amounts Incorrect Logical Error Resolved
Description
Proof of concept: PoC
In several cases the
getAdjustedLongAndShortTokenAmountsfunction reverts or returns a nonsensical result:poolLongTokenAmountandpoolShortTokenAmountaccess the pool amount for the same token aslongTokenandshortTokenare the same. As a result, the pool amounts are equal and theelsecase will always be entered.- On line 387, when
uint256 diff = poolLongTokenAmount - poolShortTokenAmountis performed, the larger value is always subtracted from the smaller value causing underflow. - On line 396, when
uint256 diff = poolShortTokenAmount - poolLongTokenAmountis performed, the larger or equal value is being subtracted from the smaller value which will underflow in most cases. - On line 400,
adjustedLongTokenAmount - longTokenAmount - adjustedShortTokenAmountdoes not set theadjustedLongTokenAmountbut rather computes the result of subtraction.
Recommendation
Refactor the
getAdjustedLongAndShortTokenAmountsfunction to address the above problems.Resolution
GMX Team: The
getAdjustedLongAndShortTokenAmountsfunction was removed. -
MKTU-7 High Total Borrowing Fees Outdated Logical Error Resolved
Description
Proof of concept: PoC
The
getTotalBorrowingFeesfunction, which returns the pending borrowing fees, is out of date as it uses an outdatedcumulativeBorrowingFactorrather than getting the updated factor withgetNextCumulativeBorrowingFactor.As a result, LPs who withdraw will have their market tokens worth less than they should be as the pending fees aren’t reflected in the pool value.
Additionally, this introduces the opportunity for arbitrages that take advantage of the stepwise increase in
borrowingFeesby forcing an update with a trivialMarketIncreaseorder.Recommendation
Use
getNextCumulativeBorrowingFactorinstead ofgetCumulativeBorrowingFactorto get the latest pending borrowing fees.Resolution
GMX Team: The recommendation was implemented.
-
POSU-1 High Profit Included In Remaining Collateral Logical Error Acknowledged
Description
Proof of concept: PoC
The PnL of a position is added to the current collateral when calculating the
remainingCollateralUsd. This is logically sound when a trader’s PnL is negative, as their losses will be subtracted from their collateral upon closing their position.However, when a position is in profit, there is no effect on the position’s collateral since the profits come from the pool. In the case where positions are in profit, adding the profit to the
remainingCollateralUsdmisrepresents how much collateral value actually remains.Furthermore, if a position is profitable, this profit can be used as collateral to continue to increase the position size. This allows trader’s to open positions with far greater leverage than the
minCollateralFactor.Recommendation
Do not count profit as a part of the remaining collateral of a position.
Resolution
GMX Team: This is the desired behavior.
-
GLOBAL-5 High Reference Exchange Manipulation Protocol Manipulation Acknowledged
Description
An attacker with enough size can manipulate the reference exchanges to influence the median price and take advantage of the price movements.
Recommendation
Carefully monitor the protocol and adjust parameters such as OI caps accordingly. Furthermore, use enough reference exchanges so the median is less likely to be affected by price outliers.
Resolution
GMX Team: Acknowledged.
-
ORDH-2 Medium Frozen Orders Cannot Be Simulated Logical Error Acknowledged
Description
Proof of concept: PoC
In the event that an order is frozen it can no longer be simulated to check for validity since the
msg.senderwill not be a frozen order keeper.Recommendation
Allow the simulation to bypass the
_validateFrozenOrderKeeperauthentication check.Resolution
GMX Team: Acknowledged.
-
TU-1 Medium Call Return Value Gas Manipulation Gas Manipulation Resolved
Description
In the
withdrawAndSendNativeTokenfunction,.callis used to send ether to thereceiveraddress. Althoughbytes memory datais commented out, it will still be loaded into memory.A malicious
receivermay load unexpectedly large return data into memory and potentially cause the keeper to expend more gas than expected.The size of the returned data that the
receiveris able to generate is however constricted by thegasLimit, but this form of manipulation may still pose a risk to the system.Recommendation
Utilize a low level call to avoid loading the returned data into memory:
assembly { success := call(gasLimit, receiver, amount, 0, 0, 0, 0) }
Resolution
GMX Team: The recommendation was implemented.
-
ORDH-3 Medium Short Term Risk Free Trade With Limit Orders Protocol Manipulation Acknowledged
Description
Proof of concept: PoC
A malicious trader may be able to execute a profitable short-term risk-free trade by creating a limit order, observing the price it will be executed at and optionally front-running the execution to update or cancel the order.
This way the order is cancelled/frozen if price doesn’t move in the a direction that benefits the trader in the blocks between where the execution price is from and the current execution block.
Recommendation
Ensure order fees are sufficient to invalidate short term risk-free trades. Otherwise, do not allow users to decide whether or not their order is executed by cancelling or updating the order right before execution.
Resolution
GMX Team: Acknowledged.
-
ADLU-1 Medium Direct Use Of block.number Logical Error Resolved
Description
block.numberis used tosetLatestAdlBlockrather thanChain.currentBlockNumber(). This could yield unexpected behavior as thearbSys.arbBlockNumber()may differ from theblock.number.Recommendation
Utilize the
Chain.currentBlockNumber()when setting the latest ADL block.Resolution
GMX Team: The recommendation was implemented.
-
EDPU-2 Medium No Pool Amount Validation For Positive Impact Logical Error Resolved
Description
The
validatePoolAmountvalidation check can be circumvented for the_params.tokenOutif a user receives apositiveImpactAmountwhich comes in the form oftokenOutthat pushes the pool amount fortokenOutover themaxPoolAmount.Recommendation
Validate that the
_params.tokenOutpool amount is still within the valid range by usingvalidatePoolAmountin the case where the user is positively impacted.Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-6 Medium Block Stuffing Attack Gas Manipulation Acknowledged
Description
Order execution takes ~2,000,000 gas units for a vanilla execution without a
swapPathorcallbackContract. On certain chains, such as Avalanche C-chain, the block gas limit can be close to how much gas it takes for order executions. This makes the protocol susceptible to a block stuffing attack.For example, this transaction on the Avalanche Fuji testnet took over 4,000,000 gas units which is greater than 50% of the gas limit of 8,000,000. An attacker may choose to stuff blocks to delay the execution of their order until the current price moves favorably.
Recommendation
Consider gas optimization strategies alongside measures to remove stale execution tx’s from the mempool to prevent such manipulations.
Resolution
GMX Team: Acknowledged.
-
SWPU-1 Medium Lack Of Validation For Homogenous Markets Logical Error Resolved
Description
For markets where the
shortTokenis the same as thelongToken, thevalidateReservefunction call will only validate reserves for longs. This is becausecache.tokenOut == _params.market.longTokenwill always betrue.Recommendation
For markets where the
shortTokenis the same as thelongToken, be sure to validate the reserves for both longs and shorts.Resolution
GMX Team: These markets should never be used in the swap function, an explicit validation for this was implemented.
-
GSU-2 Medium Gas Price Deficit Gas Prices Acknowledged
Description
In the
validateExecutionFeefunction, the currenttx.gaspriceis used to estimate the gas price in the block of the execution. However the currenttx.gaspricecan be significantly different from thetx.gaspriceactually experienced in the block of execution.This allows the keeper to expend more gas than the
executionFeein the case where thetx.gaspriceis greater in the block of execution.Recommendation
Be wary of the potential
gaspricedifference and set theESTIMATED_GAS_FEE_MULTIPLIER_FACTORaccordingly.Resolution
GMX Team: Acknowledged.
-
DPCU-3 Medium swapProfitToCollateralToken Invalid Impact Accounting Error Resolved
Description
When the
swapProfitToCollateralTokenswap is performed, thepnlAmountForPoolhas not yet been decremented from thepoolAmount. Therefore the price impact calculation as well as subsequent validation checks duringswapProfitToCollateralTokenare based on thepnlAmountForPoolnot being withdrawn from thepoolAmount, but yet still being swapped.This leads to inaccurate price impact being applied during the swap as well as validation that is hinged upon a temporary invalid state of the pool accounting.
Recommendation
In the case where the
swapProfitToCollateralTokenswap is performed, account for the user’s profit tokens first being removed from the pool before they are used to swap in the pool.Resolution
GMX Team: A pool adjustment key was introduced to account for the intermediate balance update.
-
WTDU-1 Medium Users Can Game Withdrawals Protocol Manipulation Resolved
Description
A malicious user can front-run the execution of their withdrawal and send their market tokens to another address so that the withdrawal execution fails.
The attacker can observe if price moved in their favor between the block where the prices are provided from and the block where their withdrawal execution is happening and decide if they would like their withdrawal to succeed or fail.
An attacker can leverage this using the swaps at the end of a withdrawal to capitalize on outdated prices for any assets in the
longTokenSwapPathorshortTokenSwapPath.Recommendation
Consider transferring the user’s market tokens to a
WithdrawalVaultupon the withdrawal creation, similar to deposits and orders. Otherwise ensure that the withdrawal fees invalidate any possible risk-free trade that could be made.Resolution
GMX Team: The recommendation was implemented.
-
DPCU-4 Medium indexToken vs pnlToken Arbitrage Arbitrage Opportunity Resolved
Description
For markets where the
pnlTokencan be the same as theindexToken, there are cases where theindexTokenandpnlTokenare valued differently and users benefit from this difference at the market’s expense.Consider a
StopLossDecreaseorder for a long in profit, thepnlTokenis the same as theindexToken.The user's pnl calculation can value the
indexTokenat theacceptablePrice, say $5495. However the resultingvalues.positionPnlUsdis converted to thepnlTokenat thesecondaryPrice, say $5490.This yields a delta of tokens that was not originally factored into the PnL for the market, so the market experiences slightly more loss than expected and the user gains slightly more than expected.
Recommendation
For cases where the
pnlTokenis the same as theindexToken, consider using theexecutionPriceto denominate thevalues.pnlAmountForPool.Resolution
GMX Team: The execution price logic was refactored.
-
DPU-2 Medium Drain Keeper’s Gas Through Liquidations Gas Attack Acknowledged
Description
Proof of concept: PoC
A trader is allowed to decrease their position such that the collateral is below the minimum collateral because
shouldValidateMinCollateralUsdisfalse. However,shouldValidateMinCollateralUsdis set totruefor liquidation orders.Therefore, a trader’s decrease order can go through and their position can be liquidated right after by a liquidation keeper. An attacker may leverage this to drain the keeper of its gas by creating trivial positions and decreasing them to invalidate the minimum collateral so that they are subsequently liquidated.
Recommendation
Always validate the minimum collateral amount when decreasing or increasing a position.
Resolution
GMX Team: Acknowledged.
-
PPU-3 Medium No Lower Bound On Virtual Inventory Price Impact Logical Error Resolved
Description
In the
getPriceImpactUsdfunction thepriceImpactUsdForVirtualInventoryis asserted to be<=thethresholdPriceImpactUsd, otherwise the normalpriceImpactUsdis used.This however allows the
priceImpactUsdForVirtualInventoryto negatively impact users without bound. This way malicious actors in other markets are able to grief users using the samepriceImpactUsdForVirtualInventory.Recommendation
Modify the
priceImpactUsdForVirtualInventory > thresholdPriceImpactUsdcomparison topriceImpactUsdForVirtualInventory < thresholdPriceImpactUsd.Resolution
GMX Team: The misleading check was removed.
-
PPU-4 Medium borrowingFeeAmountForFeeReceiver Double Counted Double Counting Resolved
Description
The
fees.totalNetCostAmountincludes both thefees.feeReceiverAmountand thefees.borrowingFeeAmount. TheborrowingFeeAmountis comprised of both the borrowing fees for the pool and for thefeeReceiver.The
fees.feeReceiverAmountalso includes the borrowing fees for thefeeReceiver, therefore theborrowingFeeAmountForFeeReceiveramount is accounted for twice in thefees.totalNetCostAmount.Recommendation
Only account for the
borrowingFeeAmountForFeeReceiveronce in thefees.totalNetCostAmount.Resolution
GMX Team: The recommendation was implemented.
-
MKTU-8 Medium Precision Loss For Funding Fees Precision Loss Resolved
Description
When computing the
cache.fundingUsd,a USD amount with 30 decimals of precision is divided by 1e30:cache.sizeOfLargerSide / Precision.FLOAT_PRECISION.This results in precision loss on the order of magnitude of tens of cents for the distribution of funding fees.
Recommendation
Consider if this magnitude of precision loss is acceptable and adjust the calculation if it isn’t.
Resolution
GMX Team: The
fundingUsdcalculation was adjusted. -
SPRU-1 Medium Double Counting Swap Imbalance Double Counting Resolved
Description
When calculating the price impact USD value for a swap, the
thresholdPriceImpactUsdis based on theparams.usdDeltaForTokenA.abs()andparams.usdDeltaForTokenB.abs(). However these values will always have the same magnitude during a swap, therefore the user’s swap USD value will be double counted.If the configured
thresholdImpactFactorForVirtualInventoryis intended to be 70% of the user’s swap USD value, it will instead account for 140% of the user’s swap USD value.Recommendation
Only base the
thresholdPriceImpactUsdon a single token side ofusdDeltafor swaps.Resolution
GMX Team: The recommendation was implemented.
-
DPU-3 Medium Position Unexpectedly Closed Unexpected Behavior Resolved
Description
In the case where a user’s collateral is deemed to be insufficient after removing the
initialCollateralDeltaAmount, theinitialCollateralDeltaAmountis set to 0.However if the
estimatedRemainingCollateralUsd, which was based upon theinitialCollateralDeltaAmountbeing removed from the position’s collateral, is smaller than theMIN_COLLATERAL_USDthe position will still be closed.This is unexpected behavior as the
initialCollateralDeltaAmounthas been set to 0 so the position’s collateral will no longer be less than theMIN_COLLATERAL_USD.Recommendation
Do not close the user’s position in the case where the
initialCollateralDeltaAmountis set to 0.Resolution
GMX Team: The
initialCollateralDeltaAmountis now added back to theestimatedRemainingCollateralUsdif it is set to 0. -
GLOBAL-7 Medium Funding Fees Accumulate In Disabled Markets Unexpected Behavior Acknowledged
Description
If increase and decrease position functionalities are disabled, funding fees will continue to accumulate as time goes by. Traders will be charged unexpected fees when trading resumes.
Recommendation
Consider pausing funding fee accumulation when trading is disabled.
Resolution
GMX Team: Acknowledged.
-
ADLU-2 Medium Latest ADL Block Updated Without ADL State Change Logical Error Acknowledged
Description
The latest ADL block is updated each time
updateAdlStateis called even if the ADL state was not enabled or changed. This may lead to a keeper continuously updating ADL block and preventing much needed ADL decrease orders from going through.Recommendation
Consider using
if (shouldEnableAdl) setLatestAdlBlock(dataStore, market, isLong,Chain.currentBlockNumber()).Otherwise, if current functionality is intended, add more documentation surrounding ADL block updates.
Resolution
GMX Team: The current functionality is intended.
-
ORDH-4 Medium Risk-Free Trade With Disabled Feature Protocol Manipulation Acknowledged
Description
If a limit order is in the
dataStoreand the trading features are disabled, then the possibility of a risk-free trade arises.Right before a feature is re-enabled, if prices have moved against the trader, the trader may cancel or update their limit order. Otherwise, the order can execute with outdated prices.
Recommendation
Do not revert on the
FeatureUtils.DisabledFeatureerror, but rather freeze or cancel these limit orders.Resolution
GMX Team: Acknowledged.
-
DPCU-5 Medium Remaining Collateral Adjusted To Revert Logical Error Acknowledged
Description
When
values.remainingCollateralAmountis less than or equal tocollateralCache.adjustedPriceImpactDiffAmount,values.remainingCollateralAmountis set to 0 and thecollateralCache.adjustedPriceImpactDiffAmountis placed in the holding area.The position is stamped with a collateral amount of 0 in
DecreasePositionUtils.solon line 195. OncevalidatePositionis entered,validateNonEmptyPositionwill be called which will revert due to the 0 collateral amount with theEmptyPositionerror.Overall, the position’s
remainingCollateralis updated only to subsequently revert whenvalidatePositionis called. Furthermore, this may open the market to scenarios where a user can influence when their order is executed and create a risk-free trade as the order stays in the store with the sameupdatedAtBlock.Recommendation
Do not set the
remainingCollateralAmountto 0 only to have the order revert later on with anEmptyPositionerror. Instead consider leaving someremainingCollateralor reverting with a separate error so that the order is cancelled or frozen.Resolution
GMX Team: Acknowledged.
-
WTDU-2 Medium Swap Required To Specify Output Amount Slippage Acknowledged
Description
A liquidity provider is unable to utilize the
minOutputAmountfunctionality without performing a swap. This may lead LPs to get less output than intended and/or confusion with the existingminLongTokenAmountandminShortTokenAmountusage in swap.Recommendation
Consider adding the functionality to specify the minimum output amounts without swapping.
Resolution
GMX Team: Acknowledged.
-
GLOBAL-8 Medium Lack of Slippage Protection When Swapping Slippage Resolved
Description
The following places lack slippage protection and may lead to loss of assets for a user:
DecreasePositionCollateralUtils.solLine 392 and Line 434 use 0 as theminOutputAmountwhich may unexpectedly reduce the profit for a trader.ExecuteDepositUtils.solLine 432 which may reduce the amount of market tokens the user obtains although risk may be limited by specifyingminMarketTokens.
Recommendation
Consider adding the functionality to specify the minimum output amounts and/or further document this behavior.
Resolution
GMX Team: The recommendation was implemented.
-
GSU-3 Medium No Way For Users To Claim Excess Execution Fee Lost Funds Resolved
Description
handleExcessExecutionFeedoes not allow users to claim the excessexecutionFeethat they may have sent. Currently the excess is simply sent to a holding address with no accounting of which user is in excess or by how much and there is no way to claim the excess fee.Recommendation
Either make a way for users to claim these tokens or ensure it is well documented and explicit that these tokens will be lost for the user.
Resolution
GMX Team: The excess execution fee logic was removed.
-
ADLU-3 Medium Two Separate ADL Factors Logical Error Resolved
Description
Keys.MAX_PNL_FACTORis used when checking if the PnL factor for ADL is exceeded instead ofKeys.MAX_PNL_FACTOR_FOR_ADLas inAdlHandler.solline 123. This can lead to ADL being enabled and not going through, or ADL being consistently disabled due to misconfiguration between the two factors.Recommendation
Use the same factor key for the same validation. If the difference is intended for finer and more precise protocol control, document such behavior.
Resolution
GMX Team: The recommendation was implemented.
-
SWPU-2 Medium Swaps Prevented When They Improve The Pool Logical Error Resolved
Description
When performing a swap,
Keys.MAX_PNL_FACTOR_FOR_WITHDRAWALSis used to check whether the currentpnlToPoolFactorexceeds the the maximum PnL factor for withdrawals, which is the strictest (lowest) maximumpnlToPoolFactor.When performing a swap, the
tokenInis deposited andtokenOutis withdrawn. By treating both the “deposit” and “withdrawal” with the same withdrawalpnlToPoolFactorthreshold, it can potentially prevent swaps that will improve the currentpnlToPoolFactoron a particular side.Recommendation
Consider validating
tokenInagainst theMAX_PNL_FACTOR_FOR_DEPOSITSandtokenOutagainstMAX_PNL_FACTOR_FOR_WITHDRAWALS.Resolution
GMX Team: The recommendation was implemented.
-
ORDH-5 Medium Gas Used Is Overestimated Logical Error Resolved
Description
When passing the
startingGasto thethis._executeOrderexternal call, it is assumed that all of thestartingGasis available for the execution of the external call. However, due to the 63/64 rule, only 63/64 of thestartingGaswill be available in the subsequent call tothis._executeOrder.Therefore when the
executionFeeis paid in the external call tothis._executeOrder, the gas used will be overestimated, and the user will be errantly charged for a false 1/64 expenditure.Recommendation
Account for the 63/64 rule when estimating the gas consumption.
Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-9 Medium Pool State Leading To Withdrawals Being Bricked Trapped Funds Acknowledged
Description
Proof of concept: PoC
With a range between the
MAX_PNL_FACTOR_FOR_WITHDRAWALSand theMAX_PNL_FACTOR_FOR_ADL, if the profit in the pool exceeds thepnlToPoolFactorfor withdrawals but is not high enough to trigger ADL, withdrawals for LPs will be bricked until users deposit more tokens into the pool and a trader decides to close their profitable position.Recommendation
Ensure that this risk is well communicated. Additionally consider setting the
MAX_PNL_FACTOR_FOR_WITHDRAWALSclose to theMAX_PNL_FACTOR_FOR_ADLto limit this scenario.Resolution
GMX Team: Acknowledged.
-
EDPU-3 Medium Market Token Price Below Allowed Amount Logical Error Acknowledged
Description
Proof of concept: PoC
Deposits are restricted if the
MAX_PNL_FACTOR_FOR_DEPOSITSis exceeded since the market token price is below the “allowed” amount. However, market token price can continue to decrease after ADL, and deposits can once again resume.Recommendation
Do not count on this minimum bound for market token price and be sure to document that the price can keep dropping below the asserted “allowed” amount.
Resolution
GMX Team: Documentation was added.
-
CLC-1 Medium boundedSub Can Underflow Logical Error Resolved
Description
The
boundedSubfunction does not correctly prevent underflow.For example,
boundedSub(type(int256).min, 1)causes an underflow and reverts because the condition checkif (a < 0 && b <= type(int256).min - a)is incorrect.This poses inherent risk for the future use of
boundedSubin this codebase and others that may adopt it.Recommendation
Replace with
if (a < 0 && b >= a - type(int256).min)orif (a < 0 && -b <= type(int256).min - a).Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-10 Medium Double Fee May Make A Position Liquidatable Double Counting Resolved
Description
Proof of concept: PoC
When increasing or decreasing a position, fees are calculated based on the size of the order and then taken out of the collateral.
However, when validating the position with
validatePositionand checkingisPositionLiquidatable, fees are calculated and applied a second time. This further reduces how much collateral the position has during this validation. This can prevent increasing or decreasing a position as theisPositionLiquidatablecheck would fail unexpectedly.Recommendation
Check if the position is liquidatable prior to fees being paid and taken out of the collateral.
Resolution
GMX Team: The recommendation was implemented.
-
PPU-5 Low Borrowing Fees Maximized Twice Logical Error Resolved
Description
When updating the
cumulativeBorrowingFactor, the fees are maximized in the protocol’s favor as can be seen ingetBorrowingFactorPerSecond. ThepoolUsduses theminprice and thereservedUsduses themaxprice.However, in
getPositionFees, the amount of collateral tokens is calculated from the borrowing fees using theminprice. Then, that amount will be multiplied by themaxprice of the collateral token. Therefore the resulting borrowing fee is maximized twice. This means the borrowing fees will be larger than expected, and the position more likely to be liquidated.An explicative example:
- A collateral token has a min-max range of $1-$2.
- The
borrowingFeeis $100 which is already maximized. - The resulting
fees.borrowingFeeAmountis $100 / $1 = 100 collateral tokens. - The
fees.totalNetCostUsdcalculation multiplies 100 * $2 = $200 in borrowing fees.
Therefore, what was originally a $100 fee turns into a $200 fee.
Recommendation
Use the direct value returned from the
getBorrowingFactorPerSecondfunction for thefees.totalNetCostUsd.Resolution
GMX Team: The maximization logic was refactored.
-
POSU-2 Low Unexpected Closing Of Positions Logical Error Acknowledged
Description
The
minCollateralFactoris derived from the current open interest such that the remaining collateral of a position is positively correlated with open interest.E.g. as there is more open interest in a market, more remaining collateral is required to be considered sufficient. This prevents smaller positions from being opened in markets with high open interest.
Additionally, users who originally opened positions in markets before the open interest had grown may find themselves unable to decrease their position collateral without closing their entire position.
Recommendation
Reconsider if these externalities are desired and if so make sure they are well documented.
Resolution
GMX Team: This behavior is desired.
-
POSU-3 Low Swapped Parameters Typo Resolved
Description
params.position.borrowingFactor()andparams.position.sizeInUsd()are swapped asMarketUtils.updateTotalBorrowingtakes theprevPositionSizeInUsdfollowed by theprevPositionBorrowingFactor. The end result is the same because multiplication is commutative, but poses a risk for future changes.Recommendation
Flip the parameters so they are correctly ordered.
Resolution
GMX Team: The recommendation was implemented.
-
WTDU-3 Low Overflow Risk Overflow Acknowledged
Description
When making a withdrawal, the
_getOutputAmountsfunction multiplies two float precision USD amounts. This multiplication can result in overflow when both USD amounts are on the order of hundreds of millions. E.g. $600,000,000 * $200,000,000 will overflow and revert.Recommendation
Be aware of this overflow risk, and consider altering the arithmetic if values of this size are expected.
Resolution
GMX Team: Acknowledged.
-
DPU-4 Low Affiliate Rewards Upon Liquidation Incentives Acknowledged
Description
Affiliates still receive their
fees.referral.affiliateRewardAmountupon liquidation because thehandleReferralfunction is always called in thedecreasePositionfunction.Recommendation
Consider whether this is desired behavior. If not, do not call the
handleReferralin the case of liquidations.Resolution
GMX Team: This is the desired behavior.
-
DPU-5 Low Worst Case Estimation Unused Validation Resolved
Description
cache.prices.indexTokenPrice.midPrice()is used to estimate the position’s PnL forcache.estimatedPositionPnlUsd.However, it may make more sense to use
prices.indexTokenPrice.pickPriceForPnlas inisPositionLiquidatableto get the worst-case scenario price for estimation purposes.Recommendation
Consider switching
cache.prices.indexTokenPrice.midPrice()toprices.indexTokenPrice.pickPriceForPnl(isLong, false).Resolution
GMX Team: The recommendation was implemented.
-
POSU-4 Low Funding Fees Sent To Receiver Unexpected Behavior Resolved
Description
In the
incrementClaimableFundingAmountfunction, the claimable funding fees are incremented for thereceiverrather than the position owner. This could be unexpected and may lead to loss of funds in the event that the receiver is a contract.Recommendation
Consider if the
position.account()should receive the claimable funding fees rather than theparams.order.receiver().Resolution
GMX Team: The recommendation was implemented.
-
PPU-6 Low Sandwich Attack For Price Impact Protocol Manipulation Acknowledged
Description
A trader may perform a weak version of a sandwich attack on large
LimitIncreaseorders to benefit from thepriceImpactin the following way:- A malicious trader observes that the
triggerPriceis approaching for aLimitIncreaseorder. - The malicious trader creates a
MarketIncreaseorder that will balance the open interest in the pool to receive positive price impact. - The
LimitIncreaseorder is executed and gets negatively price impacted because it unbalances the pool. - The malicious trader creates a
MarketDecreaseorder that will rebalance the pool and receive positive impact.
Recommendation
There are levers in place to limit the scope of these sandwich attacks such as the two-step execution system and the
acceptablePrice, but nonetheless the risk of such an attack should be well documented.Resolution
GMX Team: Acknowledged.
- A malicious trader observes that the
-
IOU-1 Low Limit Increase With Swap Path May Be Griefed Protocol Manipulation Acknowledged
Description
Proof of concept: PoC
Malicious traders can stop
LimitIncreaseorders from getting filled if the order has aswapPath.A malicious trader can observe that the
triggerPriceis approaching for theLimitIncreaseand then create their ownMarketSwapthat removes the necessary liquidity to execute theswapPathof theLimitIncreaseorder.Recommendation
Be aware and document that user’s
LimitIncreaseorders can be fail due to theirswapPath.Resolution
GMX Team: Acknowledged.
-
SWPU-3 Low Event Getting Incorrect Value Logical Error Resolved
Description
In the case where the swap is negatively impacted, the
cache.amountInincludes thenegativeImpactAmount. This misrepresents theamountInAfterFeesin theemitSwapInfofunction.Recommendation
Do not include negative impact in the
amountInAfterFeesthat is provided to theemitSwapInfofunction.Resolution
GMX Team: The recommendation was implemented.
-
DPCU-6 Low Lost Funding Fees Lost Funds Resolved
Description
In the event that a user’s position is liquidated with negative collateral, an empty
PositionFeesobject is returned from thegetLiquidationValuesfunction. This means that theclaimableLongTokenAmountandclaimableShortTokenAmountare reset to 0.In this case any funding fees that the trader had accumulated are lost.
Recommendation
Consider if this is the expected behavior. If not, maintain the existing
claimableLongTokenAmountandclaimableShortTokenAmountin the newPositionFeesobject returned fromgetLiquidationValues.Resolution
GMX Team: The recommendation was implemented.
-
TIME-1 Low Bespoke Key Used Logical Error Resolved
Description
The action key
signalSetPriceFeedkey should besetPriceFeedto match up with the other key patterns.Additionally, when performing the
_validateAndClearAction, the label when validating & clearing should besetPriceFeedAfterSignalon line 209 as well.Recommendation
Update the keys as recommended.
Resolution
GMX Team: The recommendation was implemented.
-
CLC-2 Low RoundUpDivision Rounds Down Instead Of Up Documentation Resolved
Description
The
roundUpDivisionfunction rounds down instead of up whenais negative.Recommendation
Add documentation that this function rounds up purely the magnitude of the integer
a.Resolution
GMX Team: The function was renamed to
roundUpMagnitudeDivision. -
ORDH-6 Low Limit Order Cancellation Logic Protocol Manipulation Acknowledged
Description
When markets are disabled, limit orders are not cancelled but frozen. This opens the opportunity for the frozen order keeper to possibly execute orders with outdated prices when a market is re-enabled. The use of outdated prices could lead to risk-free trade opportunities.
Recommendation
Consider adding cancellation logic for limit orders that revert due to disabled markets.
Resolution
GMX Team: Acknowledged.
-
OCLU-1 Low Misnamed Variable Readability Resolved
Description
The
blockNumbervariable represents a timestamp value rather than a block number value.Recommendation
Rename variable to something more fitting.
Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-11 Low Additional Feature Controls Controls Acknowledged
Description
It may be prudent to add features that can be disabled for things like the
DecreasePositionSwapTypes.Recommendation
Consider adding additional features that can be disabled.
Resolution
GMX Team: Acknowledged.
-
SWPU-4 Low Missing Check For tokenIn Validation Acknowledged
Description
It is possible to execute a swap with 0
tokenInand a non-zeroswapPathwhich is just a waste of resources.Recommendation
Consider adding
tokenIn> 0 as a validation.Resolution
GMX Team: Acknowledged.
-
OCL-1 Low Unsorted Max Oracle Block Numbers Validation Acknowledged
Description
It is possible for the max oracle block numbers to be unsorted unlike the min oracle block numbers. This is because the only requirement for the max block numbers is that they are at least as large as the min oracle block numbers.
For example, the keeper may pass:
min block #’s: [5,5] max block #’s [6, 5]
Recommendation
Validate that the max oracle block numbers are ascending.
Resolution
GMX Team: Acknowledged.
-
PREC-1 Low SafeCast Revert Documentation Acknowledged
Description
Calculating the
resultwill be safe as long asvalueis<= 10^47. However, theresultmay not fit in aint256leading to a revertSafeCast: value doesn't fit in an int256For example, the inputs 57923633301321315440238040719798086540604777067 and 1 yield a
SafeCastrevert.Recommendation
Document such behavior.
Resolution
GMX Team: Acknowledged.
-
BOU-1 Low setExactOrderPrice Using Older Price Logical Error Acknowledged
Description
In the event that the oracle provides multiple prices for a single token during the execution of a market order or a liquidation, the
customPriceassigned insetExactOrderPrice(primaryPrice) would differ from the price retrieved and used fromgetMarketPricesForPosition(secondaryPrice).The price used to validate liquidation (
secondaryPrice) and the price used to execute the liquidation (primaryPrice) would be different.Recommendation
Consider using the
secondaryPricefor market orders in the event that a range is errantly provided.Resolution
GMX Team: Acknowledged.
-
BNK-1 Low Future Proof Receive Function Future Proofing Acknowledged
Description
Popular wrapped network tokens like WAVAX and WFTM which the synthetics exchange will likely interact with use
.transferto withdraw native tokens to the caller.The Bank contract’s receive function does not currently consume more than 2300 gas, but gas consumption for opcodes like SLOAD which are being used in the receive function are subject to change over time which may cause the receive function to revert when called with a
.transfer.(See Berlin Hardfork)
In such a scenario the exchange would be unable to withdraw it’s WNT balance into NT and the receive failure could open up opportunities for risk free trading exploits.
Recommendation
Be wary of this possibility and have a plan in the event that gas costs are updated.
Resolution
GMX Team: Acknowledged.
-
DPU-6 Low Inaccurate Fees May Be Emitted Events Resolved
Description
The
fees.totalNetCostAmountis misrepresented inemitPositionFeesCollectedsince the fee is reduced by the profit amount in theprocessCollateralfunction.Recommendation
Account for the fees being reduced by profits in the event.
Resolution
GMX Team: The recommendation was implemented.
-
OCL-2 Low Missing Check Validation Acknowledged
Description
It is checked that the
oracleTimestampwas set not too far in the past but there is no check to ensure that theoracleTimestampis not set in the future.Recommendation
Add a check that validates that the
oracleTimestampis not in the future.Resolution
GMX Team: Acknowledged.
-
DPU-7 Low Collateral May Not Be Sufficient Logical Error Acknowledged
Description
The position’s collateral still may not be considered sufficient even after the
initialCollateralDeltaAmountis set to 0.Recommendation
Consider whether the order should still be allowed to execute in the case where the position’s collateral is still not sufficient even when the
initialCollateralDeltaAmountis 0. If not, revert with an error.Resolution
GMX Team: Acknowledged.
-
MKTU-9 Low Duplicated Code Optimization Resolved
Description
The
validateEnabledMarketfunction can be reused once the market is obtained from the address instead of duplicating code.Recommendation
Consider implementing the above suggestion.
Resolution
GMX Team: The recommendation was implemented.
-
PPU-7 Low Variable Reuse Optimization Resolved
Description
The
fees.feeAmountForPoolcan be reused in thefees.totalNetCostAmountcalculation.Recommendation
Consider implementing the above suggestion.
Resolution
GMX Team: The recommendation was implemented.
-
POSU-5 Low Use Cheaper Branch Gas Optimization Acknowledged
Description
The
priceImpactUsd > 0check can include 0 to save gas from theelsecase.Recommendation
Update the conditional to
priceImpactUsd >= 0.Resolution
GMX Team: Acknowledged.
-
GLOBAL-12 Low Internal Library Functions Visibility Modifiers Acknowledged
Description
Library functions throughout could be made
internalto save gas.Recommendation
Make as many library functions
internalas possible while staying within contract bytecode deployment limits.Resolution
GMX Team: Acknowledged.
-
DPCU-7 Low Use Cached Variable Optimization Resolved
Description
The
initialCollateralDeltaAmountvariable is left unused and the attribute is instead repeatedly accessed directly from the order.Recommendation
Use the cached
initialCollateralDeltaAmountor remove it.Resolution
GMX Team: The recommendation was implemented.
-
POSU-6 Low Function Reuse Optimization Resolved
Description
The
sizeDeltaUsdcalculation for shorts can be replaced with thegetSizeDeltaInTokensfunction.Recommendation
Consider implementing the above suggestion.
Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-13 Low Initialization of Default Values Optimization Acknowledged
Description
Throughout the codebase the index for many for-loops is initialized to 0 which is the default
uintvalue. Avoid the unnecessary initialization and allow the default values to be implicitly assigned to theseuintvariables:- FeeHandler.sol::37
- MarketUtils.sol::1859
- Oracle.sol::235
- Oracle.sol::287
- Oracle.sol::440
- Oracle.sol::476
- Oracle.sol::494
- Oracle.sol::562
- OracleModule.sol::61
- OracleModule.sol::67
- OracleUtils.sol::195
- ExchangeRouter.sol::283
- ExchangeRouter.sol::309
Recommendation
Do not assign these default values.
Resolution
GMX Team: Acknowledged.
-
GLOBAL-14 Low Initialization of Default Values Optimization Acknowledged
Description
Throughout the codebase the index for many for-loops is initialized to 0 which is the default
uintvalue. Avoid the unnecessary initialization and allow the default values to be implicitly assigned to theseuintvariables:- ExchangeRouter.sol::343
- SwapUtils.sol::123
- Array.sol::56
- Array.sol::73
- Array.sol::90
- Array.sol::107
- Array.sol::124
- BasicMulticall.sol::17
- PayableMulticall.sol::21
Recommendation
Do not assign these default values.
Resolution
GMX Team: Acknowledged.
-
GLOBAL-15 Low Cached Array Length Optimization Acknowledged
Description
Throughout the codebase there are many for-loops that compute the length of an array upon each iteration. Caching the length of these arrays will decrease gas costs throughout the application:
- FeeHandler.sol::37
- MarketUtils.sol::1859
- Oracle.sol::235
- Oracle.sol::287
- Oracle.sol::440
- Oracle.sol::476
- Oracle.sol::494
- Oracle.sol::562
- OracleModule.sol::61
- OracleModule.sol::67
- OracleUtils.sol::195
Recommendation
Cache the length of arrays before iterating through them.
Resolution
GMX Team: Acknowledged.
-
GLOBAL-16 Low Cached Array Length Optimization Acknowledged
Description
Throughout the codebase there are many for-loops that compute the length of an array upon each iteration. Caching the length of these arrays will decrease gas costs throughout the application:
- ExchangeRouter.sol::283
- ExchangeRouter.sol::309
- ExchangeRouter.sol::343
- SwapUtils.sol::123
- Array.sol::56
- Array.sol::73
- Array.sol::90
- Array.sol::107
- Array.sol::124
- BasicMulticall.sol::17
- PayableMulticall.sol::21
Recommendation
Cache the length of arrays before iterating through them.
Resolution
GMX Team: Acknowledged.
-
GLOBAL-17 Low Greater Than Zero Check Optimization Acknowledged
Description
Throughout the codebase
uintvariables are checked to be> 0however!= 0is a more efficient comparison to check ifuintvalues are positive. Switching to!= 0will decrease gas costs throughout the application:- ExecuteDepositUtils.sol::163
- ExecuteDepositUtils.sol::192
- ExecuteDepositUtils.sol::208
- GasUtils.sol::95
- Oracle.sol::592
- BaseOrderUtils.sol::352
- BaseOrderUtils.sol::369
- DecreaseOrderUtils.sol::57
- DecreasePositionCollateralUtils.sol::174
- DecreasePositionUtils.sol::96
- DecreasePositionUtils.sol::150
- IncreasePositionUtils.sol::151
- IncreasePositionUtils.sol::177
- PositionUtils.sol::471
- PositionUtils.sol::482
- PositionUtils.sol::535
- Precision.sol::61
Recommendation
Use
!= 0to check ifuintvalues are positive.Resolution
GMX Team: Acknowledged. 109
-
SWPU-5 Low Outdated NatSpec Documentation Resolved
Description
The NatSpec documentation for the
SwapParamsis outdated.Recommendation
Update the NatSpec to reflect the current contents of
SwapParams.Resolution
GMX Team: The recommendation was implemented.
-
BOU-2 Low Outdated NatSpec Documentation Resolved
Description
The
decreasePositionSwapTypeis missing from the NatSpec documentation for theCreateOrderParamsstruct.Recommendation
Update the NatSpec documentation for the
CreateOrderParamsstruct.Resolution
GMX Team: The recommendation was implemented.
-
MKTU-10 Low Outdated NatSpec Documentation Resolved
Description
The
timeKeyparameter is missing from the NatSpec documentation for theclaimCollateralfunction.Recommendation
Update the NatSpec documentation for the
claimCollateralfunction.Resolution
GMX Team: The recommendation was implemented.
-
POSU-7 Low Outdated NatSpec Documentation Resolved
Description
The
validatePositionfunction does not includeshouldValidateMinCollateralUsdas an @param in it’s NatSpec documentation.Recommendation
Update the NatSpec for
validatePosition.Resolution
GMX Team: The recommendation was implemented.
-
BOU-3 Low Outdated NatSpec Documentation Resolved
Description
The NatSpec documentation for the
ExecuteOrderParamsis outdated.Recommendation
Update the NatSpec to reflect the current contents of
ExecuteOrderParams.Resolution
GMX Team: The recommendation was implemented.
-
POSU-8 Low Outdated NatSpec Documentation Resolved
Description
The NatSpec documentation for the
UpdatePositionParamsis outdated.Recommendation
Update the NatSpec to reflect the current contents of
UpdatePositionParams.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.
