GMX engaged Guardian to review the security of its decentralized synthetics perpetuals exchange. From the 22nd of June to the 11th of July, a team of 5 auditors reviewed the source code in scope.
- Published
- Review window
- June 22 to July 11, 2023
- Language
- Solidity
- Chains
- Arbitrum, Avalanche
- Sector
- Perpetuals
- 1 Critical
- 3 High
- 16 Medium
- 28 Low
- 0 Informational
Scope
Test suiteGuardianAudits/GMX-6
Overview
GMX engaged Guardian to review the security of its decentralized synthetics perpetuals exchange. From the 22nd of June to the 11th of July, a team of 5 auditors reviewed the source code in scope.
Findings 48
-
DPCU-1 Critical Position fundingFeeAmountPerSize Errantly Reset Logical Error Resolved
Description
Proof of concept: PoC
When the
fees.totalCostAmountExcludingFundingis paid with any amount of secondary tokens thefeesobject is replaced with an empty instance.In this case however there can be a position that remains, and it will be stamped with a
fundingFeePerSizevalue of 0 from this emptyfeesinstance. Therefore the position must now pay all funding fees since the inception of the market.In all likelihood the position will then be immediately liquidated leading to an immediate significant loss of assets for the position that would have remained.
Recommendation
Do not reset the
fees.funding.funding.latestFundingFeeAmountPerSizeon thefeesobject when the position can remain, e.g. outside of any insolvent close.Resolution
GMX Team: The
fees.funding.funding.latestFundingFeeAmountPerSizeis no longer zeroed out. -
POSU-1 High willPositionCollateralBeSufficient Validation Bypassed Validation Acknowledged
Description
The
willPositionCollateralBeSufficientvalidation aims to decide whether or not the collateral amount that remains for a position will be sufficient for it's leverage.However in many cases this validation allows decrease orders which will put the position’s collateral below the “sufficient threshold”. This is because the
willPositionCollateralBeSufficientvalidation excludes fees that will be subtracted from the position's collateral as well as negative price impact that can be applied to the position's collateral.Recommendation
Account for fees and potentially even negative price impact in the
willPositionCollateralBeSufficientso that the validation cannot be circumvented in these cases.Resolution
GMX Team: Acknowledged.
-
ORDH-1 High Keeper Griefed With orderUpdatedAtBlock Griefing Acknowledged
Description
Orders will remain in the order store when the
OracleBlockNumbersAreSmallerThanRequirederror occurs during execution. Therefore a malicious user can cause the keeper to continuously expend gas to attempt to execute an order without requiring any additionalexecutionFee.Consider the following scenario:
- A malicious user frontrun’s the keepers execution tx and updates their order, updating the
orderUpdatedAtBlock. - Now the keeper's tx goes through all of the price setting and order execution logic up to the oracle block number validation.
- Then the order execution tx reverts, the keeper has spent a significant amount of gas but the order still remains, with the same
executionFeestill attached. - The malicious user may continue to do this and continue to gas grief the keeper.
- The malicious user can then cancel their order at any time to receive their
executionFeeback.
The scenario can be exacerbated with an order that requires many prices to be set where the malicious user includes a maximum length
swapPaththat requires many prices.Recommendation
Require a non-refundable WNT payment upon order updates to disincentivize such attacks. Otherwise consider validating the oracle block numbers for the order early on in the order execution to minimize the amount of gas that can be wasted.
Resolution
GMX Team: Acknowledged.
- A malicious user frontrun’s the keepers execution tx and updates their order, updating the
-
MKTU-1 High Borrowing Fees Avoided Due To Skip Protocol Manipulation Resolved
Description
Proof of concept: PoC
Users who have accumulated a large amount of borrowing fees can avoid paying these fees as long as the
cumulativeBorrowingFactorhas not yet been incremented andskipBorrowingFeeForSmallerSideis enabled.Consider the following scenario:
- Long OI > Short OI
- Trader A has a large long position that has accumulated a significant amount of borrowing fees. These borrowing fees have not been “solidified” by the update of any other long position. Therefore the
cumulativeBorrowingFactorhas not yet been incremented to account for Trader A’s accumulated borrowing fees since the last recordedcumulativeBorrowingFactorUpdatedAtfor longs. - Trader A opens another short position shifting the larger side to be the opposite of the first position.
- Trader A then closes the first position and pays 0 borrowing fees.
Recommendation
Do not allow previously accumulated borrowing fees to be skipped in the event that a trader forces a side to have the smaller OI. This can be achieved by updating both the long and short borrowing fees upon position increase and decrease.
Resolution
GMX Team: The recommendation was implemented.
-
IPU-1 Medium Incongruent Price Impact Logical Error Resolved
Description
The price impact amount represented by the
executionPricemay not match the price impact amount calculated. This is because the index price used to calculate the price impact amount may differ from the index price used ingetExecutionPriceForIncrease.Consider the following scenario where a trader increases a long position:
Price Impact USD = -$100; Index Price = ($50, $100)In
IncreasePosition:index price = indexTokenPrice.min = $50 price impact amount = -$100 / $50 = -2 tokensIn
BaseOrderUtils:index price = indexTokenPrice.pickPriceForPnl(isLong, true) = $100 price impact amount = -$100 / $100 = -1 tokensThe discrepancy is because $100 is used for the index price in
getExecutionPriceForIncreaserather than $50. As a result, theexecutionPricedoesn’t reflect the price impact amount which is added to thebaseSizeDeltaInTokens.Recommendation
executionPricecan simply be calculated as:executionPrice = params.order.sizeDeltaUsd() / cache.sizeDeltaInTokensto reflect the price impact amount used and then emitted in the event.
-
EDPU-1 Medium Invalid Deposit Price Impact For Homogenous Markets Logical Error Acknowledged
Description
Proof of concept: PoC
When depositing, swap impact is calculated for the
longTokenUsdandshortTokenUsdbeing deposited.However in the case of markets where
longToken == shortToken, all the value being deposited will be in thelongTokenUsd.The market will always be considered balanced to begin with since the
poolAmountis simply divided by two for both sides. Therefore every deposit will receive negative impact because each deposit is treated as if it is adding all its value to the long side and therefore unbalancing the pool.Recommendation
Skip price impact calculations when depositing into markets where
longToken == shortToken.Resolution
GMX Team: The price impact factors for homogenous markets should always be set to 0.
-
DPCU-2 Medium priceImpactDiffUsd Paid Before priceImpactUsd Prioritization Resolved
Description
During an insolvent close, the
priceImpactDiffUsdis paid at a higher priority than the negative price impact.This means there are scenarios where the position is liquidated or ADL'd and the account is credited with claimable tokens for price impact that was capped, meanwhile the base price impact, that was the uncapped portion, goes unpaid.
Ultimately this benefits the user and hurts the protocol because these funds will become claimable for the user rather than going towards the
positionImpactPoolandpoolAmountto cover as much of the price impact amount as possible.Recommendation
Pay the
priceImpactUsdbefore thepriceImpactDiffUsd.Resolution
GMX Team: The recommendation was implemented.
-
GSU-1 Medium Decrease Swap Type Not Included In Gas Estimation Gas Estimation Resolved
Description
When a decrease order contains a
decreasePositionSwapTypeother thanNoSwapit will execute an additional swap in the current market. However this additional swap is not accounted for in theestimateExecuteDecreaseOrderGasLimitgas estimation.Therefore these orders will consume gas for an extra swap that is not accounted for in the estimated gas cost.
Additionally if this extra swap were to be accounted for by default in the base
decreaseOrderGasLimit, it would be requiring users with aNoSwapto put down more initialexecutionFeethan necessary.Recommendation
Account for an additional
gasPerSwapfor decrease orders that have adecreasePositionSwapTypeother thanNoSwap.Resolution
GMX Team: The recommendation was implemented.
-
EDPU-2 Medium Subsequent Mints Cause Market Token Inflation Logical Error Resolved
Description
In the
usdToMarketTokenAmountfunction when the supply of market tokens is 0 and thepoolValueis nonzero, the resulting market token amount is thepoolValue + usdValue.However in the
_executeDepositfunction, themarketTokensSupplyandpoolValuevariables are cached and passed to theusdToMarketTokenAmountfunction twice in a row when there is positive impact.When the supply of market tokens is zero and there is some dust leftover in the
poolAmountit is possible to be positively impacted and end up with:2 * poolValue + (positiveImpactAmount.toUint256() * _params.tokenOutPrice.max) + (fees.amountAfterFees * params.tokenInPrice.min) market tokensThus resulting in a market token amount that double counts the
poolValue. The case where this market token amount inflation occurs is rare and there is no immediate financial loss or gain.However, this market token inflation is unexpected and goes against the documented goal of a 1 USD value per market token in the
usdToMarketTokenAmountfunction. This unexpected and undocumented behavior could be used to exploit systems building on top of GMX V2.Recommendation
Consider re-calculating the updated
poolValueand market token supply after the positive impact application if the initial market token supply was 0. Otherwise document this unexpected behavior.Resolution
GMX Team: Price impact is now set to zero if it is positive and there is a market token supply of 0.
-
IOU-1 Medium Overwritten Callback Contract Misconfiguration Resolved
Description
The saved callback contract is set anytime an
IncreaseOrderis processed, therefore the following unexpected scenario may arise:- A trader creates a limit increase order for their position without a callback contract.
- The trader now sends a market increase order with callback contract A to save contract A.
- The trader’s limit increase order then executes.
- The saved Callback contract is now overwritten with
address(0), and upon liquidation or ADL, no callback action occurs.
This may cause unexpected results for the trader considering the callback may be performing a useful action such as closing out a hedge/position on another platform. Additionally, the trader might only want the callback to be executed for their increase, and not on any subsequent liquidation or ADL.
Recommendation
Remove the
setSavedCallbackContractfunction from theIncreaseOrderflow so it can be set explicitly with a separate configuration function. Additionally, clearly document that there can only be 1 saved callback contract per market e.g. if a user has two positions long and short in the same market both will use the same callback contract.Resolution
GMX Team: The recommendation was implemented.
-
MKTU-2 Medium Negative Pool Value DoS DoS Acknowledged
Description
When the
result.poolValueis negative it is impossible to deposit into a market to make it useable again. There can be leftover index tokens in the position impact pool which are subtracted from the pool value. Consequently, it is possible to achieve a state where the supply of market tokens is 0, but the pool value is negative.Such a scenario would shut down the market, preventing inflow of any deposits and further usage. However, it is important to note that such a scenario would be rare.
Recommendation
Clearly document that such a scenario can occur, and monitor impact factors to help prevent such a situation from arising.
Resolution
GMX Team: Documentation will be added.
-
GLOBAL-1 Medium Issues With Equity Synthetic Tokens Stock Splits Acknowledged
Description
Equities will potentially be supported for trading, as long as they have a price feed. Potential issues arise in the case of forward stock splits, where the price per share is halved and the number of shares a user owns double. This may require an update to the size in tokens for existing positions or bespoke price logic. Other scenarios include reverse stock splits, mergers and acquisitions, etc.
Recommendation
Document protocol behavior in such scenarios and carefully monitor markets where such an event is approaching as they are announced in advance.
Resolution
GMX Team: Acknowledged.
-
DPCU-3 Medium Collateral Prioritized Over PnL Prioritization Acknowledged
Description
In the
payForCostfunction, the collateral token is prioritized over the secondary token when making payments. As a result, a user’s collateral could be unexpectedly reduced when their is substantial PnL or price impact in the secondary token available to cover the cost.This can be especially unexpected for costs such as negative price impact which would commonly be thought of as affecting the execution price and being deducted from the PnL.
Recommendation
Consider prioritizing secondary token amounts over collateral when paying amounts such as negative price impact. Otherwise, document that the user’s collateral will be prioritized over secondary PnL.
Resolution
GMX Team: Documentation will be added.
-
MKTU-3 Medium Shorts Arbitrarily Pay Stable Funding To Longs Incentives Acknowledged
Description
In the event that OI is balanced for longs/shorts, shorts will arbitrarily pay longs because of the definition of
result.longsPayShorts:result.longsPayShorts = cache.longOpenInterest > cache.shortOpenInterestNormally, the
fundingUsdwould be 0 in this case as the resultingfundingFactorPerSecondis 0 when the OI is balanced. However when there is a stable funding factor configured thefundingFactorPerSecondwill be nonzero when the OI is balanced.Therefore when there is a stable funding factor configured, shorts will arbitrarily pay longs the stable funding factor. This causes an incentive to stop shorting and join the long side to collect funding fees rather than pay them. Even though OI is already balanced, which undermines the point of funding fees.
Recommendation
Skip the update per size delta logic when there is a stable funding factor present and the long open interest is equivalent to the short open interest.
Resolution
GMX Team: Acknowledged.
-
DPCU-4 Medium Non-zero Effect On Pool Value Logical Error Acknowledged
Description
When the index token is the same as the PnL token, the positive price impact amount deducted from the position impact pool is maximized (round up division & divide by min price) meanwhile the deduction amount for the pool is minimized (round down division & divide by max price).
Similarly, when the index token is the same as the collateral token, the negative price impact amount added to the position impact pool is minimized (multiplied by the min price and divided by the max price) while the pool amount delta is not.
This creates a tendency for positive price impact to err on the side of increasing the pool value as stated in the comment on line 171. Because of the unequal effects on the impact pool amount and the pool amount there is an immediate non-zero impact on the pool value.
This behavior opens up the possibility for market depositors to manipulate price impact and absorb the position impact pool value. Such a manipulation can be straightforward when the index token is the same as either the collateral token or PnL token.
Recommendation
When the index token is the same as the collateral token or the PnL token consider using the same prices to convert usd values to token values during both positive and negative price impact accounting.
Resolution
GMX Team: Acknowledged.
-
GSU-2 Medium getExecutionGas Needs to Account for Callback Gas Logical Error Acknowledged
Description
The
GasUtils.getExecutionGasfunction sets aside aminHandleErrorGasamount to handle the subsequent error logic. However, this amount does not take into account the configured callback gas limit for an order in the event of a cancellation/freezing.Recommendation
Include the configured callback gas limit for the order being executed in the
minHandleErrorGasresult.Resolution
GMX Team: The
minHandleErrorGaswill be adjusted to account for this. -
GLOBAL-2 Medium Saved Callback Keeper Griefing Gas Griefing Acknowledged
Description
Saved callback contracts will be executed on liquidation and ADL orders which are entirely funded by the keeper/protocol without any remuneration from the user. Additionally, the saved callback contract will be given the maximum callback gas limit upon liquidation or ADL.
This way malicious traders may grief the keeper by creating numerous positions that become liquidatable simply to force the keeper to expend the maximum callback gas limit. Notice that a malicious trader may also be able to directly extract value from the keeper with gas tokens upon a liquidation or ADL callback since this execution gas is subsidized by the protocol.
Recommendation
Be aware of the potential for significant uncovered gas expenditure. Consider implementing a mechanism to remunerate the keeper for excessive callback gas expenditure from the user’s remaining collateral in the case of liquidation and ADL orders.
Resolution
GMX Team: Keeper remuneration will be implemented in a future iteration.
-
OCL-1 Medium Chainlink Feed Manipulation Protocol Manipulation Acknowledged
Description
In the event that a configured Chainlink price feed is outdated the order execution transaction will not occur due to a
PriceFeedNotUpdatedrevert.A malicious trader may observe that the price feed is outdated and submit a market order that includes a token requiring that price feed. The trader’s order will not be executed as the price feed is outdated.
The trader can then observe that the chainlink prices have been updated with a
transmittransaction and choose to cancel their order if price has not moved favorably in the time that the price feed was outdated.Recommendation
Carefully monitor outdated Chainlink price feeds and consider implementing logic to freeze/cancel orders that cannot be executed.
Resolution
GMX Team: Acknowledged.
-
MKTU-4 Medium Unwieldy Claimable Collateral Controls Protocol Manipulation Acknowledged
Description
In the
claimCollateralfunction theclaimableFactoris the maximum of theclaimableFactorForTimeandclaimableFactorForAccount. Therefore in the case where capped negative price impact is manipulated and claimable collateral ought to be used at the protocol's discretion to punish the manipulator, the controls will be insufficient.Consider the following:
- A malicious trader manipulates reference prices to take advantage of negative PI capping.
- Many other traders are capped in the same
timekeyas this one malicious trader. - The other traders ought to be able to claim their collateral at a later date, but the malicious trader should never have their
claimableFactorupdated.
The current controls are not well suited for this scenario since every non-malicious trader would have to have a manual per-account
claimableFactorForAccountconfigured. In the case where there are many innocent traders in thistimekeythis may be impractical, especially in times of market volatility. The controls should be flipped such that the single malicious trader can be punished with a more constrictiveclaimableFactorForAccount.Recommendation
Alter the
claimableFactorForAccountlogic such that it is a more constrictive threshold rather than a less constrictive one.This would likely be accompanied by a
isClaimableFactorForAccountEnabledboolean value to indicate whether theclaimableFactorForAccountought to be used in the case that it is the default value of zero.Resolution
GMX Team: If others traded during the manipulation it may make sense to cap their pnl as well.
-
GLOBAL-3 Medium Unbounded Virtual Inventory Price Impact Suggestion Acknowledged
Description
There is no lower bound on how negative price impact can be during swaps or position orders due to the virtual inventory. In virtual inventories where there are many markets, it is possible to build up large imbalances over time as users may be incentivized with positive impact from their direct pool to make deposits or orders that ultimately increase the disparity in the virtual inventory.
The resulting extreme disparity in the virtual inventory will lead to significant negative impact without bound for unsuspecting users. The most likely outcome is that deposits and orders that would compute price impact from the virtual inventory will simply be cancelled due to their acceptable price or minimum amounts being unfulfillable.
Recommendation
Consider implementing a lower bound on the magnitude of negative price impact that can be applied from the virtual inventory. Alternatively, consider creating separate price impact factors that can apply specifically to the virtual inventory calculations as the virtual inventory imbalances can be significantly larger than normal markets.
Resolution
GMX Team: Will implement capping of negative price impact in a future iteration if needed.
-
DPU-1 Low Duplicate collateralTokenPrice Fetched Optimization Resolved
Description
In
decreasePositionthere exists acache.collateralTokenPricewhich is stored at the beginning of the function execution on line 73.However this price is retrieved for a second time with
getCachedTokenPriceon line 148.Recommendation
Reuse the
cache.collateralTokenPrice.Resolution
GMX Team: The recommendation was implemented.
-
MKTU-5 Low Typo Typo Resolved
Description
The comment
if there is a stable funding factor then used that instead of the open interestmisspells “use” as “used”.Recommendation
Replace “used” with “use”.
Resolution
GMX Team: The recommendation was implemented.
-
MKTU-6 Low Redundant if Case Optimization Acknowledged
Description
The same
ifcondition relying onresult.longsPayShortsis repeated back to back. The logic in both can be deduplicated into a singleifcondition.Recommendation
Consolidate the contents of each
if (result.longsPayShorts)condition into a singleifcase.Resolution
GMX Team: Acknowledged.
-
DPCU-5 Low Lack Of Event Data Events Resolved
Description
When emitting the
emitInsufficientFundingFeePaymentevent, it may be helpful to include theamountPaidInSecondaryOutputTokenas this is useful when determining how much collateral token needs to be deposited from an insurance fund or other into a market.Recommendation
Consider adding the
amountPaidInSecondaryOutputTokenas a piece of data emitted with theemitInsufficientFundingFeePaymentfunction call.Resolution
GMX Team: The recommendation was implemented.
-
DPCU-6 Low Inefficient Price Fetching Optimization Acknowledged
Description
In the
payForCostfunction, if the collateral token amounts are insufficient to pay for the cost, the secondary price is fetched to convert the remaining cost into a secondary token amount.However the secondary token price, which is always the pnl token price, is re-fetched upon every
payForCostcall.Meanwhile there is an existing
cache.pnlTokenPriceon thePositionUtils.DecreasePositionCachememory cacheparameter.Recommendation
Accept the
pnlTokenPriceas an argument to thepayForCostfunction and pass thecache.pnlTokenPriceas the value upon eachpayForCostcall.Resolution
GMX Team: Since it is reading from memory, it should not have a large effect on gas usage.
-
DPCU-7 Low initialCollateralDeltaAmount Unexpected Adjustment Unexpected Behavior Acknowledged
Description
The comment on line 485 notes that “
the priceImpactDiffUsd has been deducted from the outputamount or the position's collateral”, but in fact thepriceImpactDiffUsdcan be deducted from thesecondaryOutputAmountin a subset of cases.Additionally, there are several cases in which the
priceImpactDiffUsdis deducted from a subset/combination of the three.In cases where the
priceImpactDiffUsdis deducted in any part from either theoutputAmountor thesecondaryOutputAmountsubtracting the fullpriceImpactDiffAmountfrom theinitialCollateralDeltaAmountdoes not accurately reflect the portion of thepriceImpactDiffUsdthat was deducted directly from the collateral. Therefore this adjustment does not make the effect on the collateral amount predictable.Recommendation
Consider adjusting the
initialCollateralDeltaAmountby the exact amount deducted from the collateral (or even just the amount paid in the collateral token to be somewhat accurate, while maintaining simplicity) when paying for thepriceImpactDiffUsdon lines 388-428.Resolution
GMX Team: Comment added.
-
GLOBAL-4 Low Empty Event Data Events Acknowledged
Description
Throughout the codebase, there are instances of
EventDatabeing declared and being returned empty.For example,
IncreaseOrderUtils.processOrderreturns empty event data. In comparisonDecreaseOrderUtils.processOrderandSwapOrderUtils.processOrderreturn populated event data.Recommendation
Ensure that event data is populated where necessary.
Resolution
GMX Team: The eventData is intentionally empty.
-
DPCU-8 Low Inaccurate Comment Comments Resolved
Description
The comment on line 101 states that “
then priceImpactUsd would be $20”, however this should refer to thepriceImpactDiffUsdinstead.Recommendation
Replace
priceImpactUsdwithpriceImpactDiffUsdin the comment.Resolution
GMX Team: The recommendation was implemented.
-
POSU-2 Low getPositionPnlUsd No Longer Needs Separate Prices Superfluous Code Resolved
Description
Since the base PnL is calculated, now independent of price impact, the
getPositionPnlUsdfunction no longer needs to use a separate index token price to compute thepoolPnlfor capping calculations.Recommendation
The
executionPricecan be renamed to theindexTokenPriceand this price can be used for all calculations.Resolution
GMX Team: The recommendation was implemented.
-
IPU-2 Low Superfluous if Case Superfluous Code Resolved
Description
The
if (cache.sizeDeltaInTokens < 0)case will never be satisfied as thecache.sizeDeltaInTokensis auint256value.Recommendation
Remove this superfluous
ifcase.Resolution
GMX Team: The
cache.sizeDeltaInTokenswas made an int. -
GLOBAL-5 Low Users Negatively Impacted By Price Spread Documentation Acknowledged
Description
When users are depositing, the tokens in the
poolAmountduring thegetPoolValueInfofunction are valued at the maximum price, meanwhile the user’s deposits are valued at the minimum price.When users are withdrawing, the tokens in the
poolAmountduring thegetPoolValueInfofunction are valued at the minimum price, meanwhile thelongTokenPoolUsd, shortTokenPoolUsdandtotalPoolUsdare valued at the maximum price.Therefore users are negatively impacted by large spreads when depositing and withdrawing from a market.
Additionally, it is noteworthy that this will in some cases leave behind a non-trivial amount of tokens in the
poolAmountafter all depositors have withdrawn.Recommendation
It should be well documented that depositors and withdrawers are negatively impacted by the price spread.
Resolution
GMX Team: Documentation will be added.
-
GLOBAL-6 Low Missing NatSpec Documentation Acknowledged
Description
Several functions throughout the codebase are missing NatSpec documentation:
getExecutionPricepayForCosthandleEarlyReturngetEmptyFees
Recommendation
Add the relevant documentation to functions where NatSpec is lacking.
Resolution
GMX Team: Will fix NatSpec in a future iteration.
-
MKTU-7 Low Stable Funding Factor Liquidation Risk Configuration Acknowledged
Description
In the event where a stable funding factor is set or unset it may cause positions to unexpectedly become liquidatable due to a stepwise increase/decrease in the factor and the resulting funding fees calculated.
Recommendation
Document that positions may unexpectedly experience an increase/decrease in funding fees due to the modification of the stable funding factor.
Resolution
GMX Team: Will add to docs.
-
ARR-1 Low Check Odd Gas Optimization Optimization Acknowledged
Description
Modulo 2 is used to determine if the length of the
arrarray is odd, however& 1is a more efficient alternative.Recommendation
Modify the check from
arr.length % 2 == 1toarr.length & 1 == 1.Resolution
GMX Team: Acknowledged.
-
GLOBAL-7 Low Liquidation Fee Suggestion Acknowledged
Description
Currently there is no additional fee that a trader incurs when they are liquidated. The trader is simply forced to settle their fees/losses. This way a liquidation is roughly equivalent to a stop loss.
This may allow traders to open high leverage positions and gain an advantage from the fact that they will simply be “stopped out” when they are liquidatable.
Additionally traders may gain an advantage by shifting the monetary impact of an insolvent close onto the protocol in the event that price gaps significantly and a high leverage position is immediately under water. Traders are not negatively impacted by liquidations so they are more likely to take on these positions and push these losses onto the pool depositors in the case of insolvent closes.
Recommendation
Consider adding a liquidation fee to make liquidations sufficiently unattractive for traders. The fee may be allocated to the pool to offset the potential financial impact of insolvent closes.
The liquidation fee may be configured to 0 in the vast majority of cases. However it may prove useful in the event of high leverage position manipulations.
Resolution
GMX Team: A liquidation fee will be added in a future iteration if required.
-
IOU-2 Low Swap Before Updating Borrowing State Protocol Manipulation Acknowledged
Description
During execution of increase orders, a swap is performed to receive the position’s collateral, shifting the pool amounts. This swap is performed before the borrowing factor is updated with
updateFundingAndBorrowingStatewhich will rely on the balance of backing tokens in the pool to compute the current borrowing fees.A trader can use the collateral token swap during increase to manipulate the balance of backing tokens and therefore minimize the borrowing fees that are recorded for their position.
Recommendation
Monitor the swap fee to dissuade any borrowing fee manipulation and document such behavior.
Resolution
GMX Team: Acknowledged.
-
GLOBAL-8 Low Floating Pragma Version Best Practices Acknowledged
Description
Throughout the codebase, the
.solfiles use a floating pragma with ^0.8.0. There is a list of known bugs in many 0.8 Solidity versions that were fixed in subsequent releases:https://github.com/ethereum/solidity/blob/develop/docs/bugs.json
Furthermore, if compiled with 0.8.20 there may be unexpected reverts when deployed as many chains still do not support the
PUSH0opcode.Recommendation
Use a static pragma.
Resolution
GMX Team: The Solidity version will be set in the hardhat config instead..
-
IPU-3 Low Unnecessary perSizeValues Initialization Optimization Acknowledged
Description
During the increase position logic, the funding
perSizevalues are stamped on a new position with zero size. However, the entireifcase andperSizeinitialization is unnecessary as the funding fees and claimable amounts will be calculated by multiplying the position size with thediffFactor.Initially the position size will be zero, resulting in 0 funding fees and claimable amounts, even with a non-zero
diffFactor. The funding feesperSizevalues will then be set for the position later on (lines 170-172). Therefore the initial stamping of the fundingperSizevalues for a brand new position are unnecessaryRecommendation
Remove the
ifcase where a new position with 0sizeInUsdhas theperSizevalues stamped.Resolution
GMX Team: Acknowledged.
-
SWPU-1 Low Misleading priceImpactUsd Emitted Events Resolved
Description
At the end of a swap, the
priceImpactUsdis emitted with theemitSwapInfofunction.However the
priceImpactUsdvalue may not be accurate to how much price impact was actually applied to the swap, depending on if the impact amount was capped by the size of the swap impact pool or not.Recommendation
Either modify the value passed to the event or add a parameter for
priceImpactAmount.Resolution
GMX Team: The recommendation was implemented.
-
POSU-3 Low Outdated Price Impact Formula Documentation Resolved
Description
According to the README, the formula for price impact calculations should be:
(initial imbalance) ^ (price impact exponent) * (price impact factor / 2) - (next imbalance) ^ (price
impact exponent) * (price impact factor / 2)However with latest changes to
PricingUtils.applyImpactFactor(), the/ 2division was removed from the formula.Recommendation
Update the formula in the documentation.
Resolution
GMX Team: The documentation was updated.
-
MKTU-8 Low Missing swapPath Validation Validation Resolved
Description
The
validateSwapPathfunction does not include validation for homogenous markets, which would cause the swap to fail automatically upon execution.Similarly, there is no validation for duplicate markets in the provided
swapPathuntil the swap is attempted during execution.Recommendation
In the
validateSwapPathfunction, verify that there are not any single token markets or duplicate markets in theswapPath.Resolution
GMX Team: Now single token markets are validated, duplicate market validation may be too gas intensive.
-
DATA-1 Low Superfluous Stack Variable Optimization Acknowledged
Description
The current value of the
uintValues[key]is cached as theuint256 currValuestack variable. However, thecurrValuevariable is only referenced once on the very next line and so therefore can be replaced with a direct access of the mapping.Recommendation
Access the mapping directly when calculating
nextUintrather than storing thecurrValue.Resolution
GMX Team: Acknowledged.
-
ERTR-1 Low ExchangeRouter Inefficient Loops Optimization Acknowledged
Description
Throughout the
ExchangeRouterseveralclaimedAmountslists are declared of with a length that is subsequently looped over. However this length is recomputed for both the list declaration as well as theforloop. This length can be cached to save gas on both the list declaration and upon each iteration of theforloop.Recommendation
Cache the array length and use this cached value in the
forloop and array declaration.Resolution
GMX Team: Acknowledged.
-
GLOBAL-9 Low Empty Swap Path For Swap Orders Optimization Acknowledged
Description
Users can submit valid swap orders that have an empty
swapPath.Recommendation
Validate that the
swapPathlength is nonzero for swap orders upon order creation.Resolution
GMX Team: Acknowledged.
-
ARR-2 Low Oracle Block Validation Optimizations Optimization Acknowledged
Description
In the
get,areEqualTo,areGreaterThan,areGreaterThanOrEqualTo,areLessThan,areLessThanOrEqualTofunctions, the length of thearrarray is not cached during theforloop execution.Additionally, the increment of
ican be replaced with anuncheckedblock wrapping++i.Recommendation
Implement the recommended optimizations.
Resolution
GMX Team: Acknowledged.
-
GLOBAL-10 Low Price Impact Incongruence Inconsistency Resolved
Description
Price impact for deposits is calculated based on the pool amount imbalance from the deposited amounts before fees are accounted for.
However price impact for swaps is calculated after fees are taken from the swapped in amount.
The known issues in
README.mdmention thatCalculation of price impact values do not account forfees, however this is directly in contradiction to the price impact calculations for swaps.Recommendation
Consider standardizing on one or the other for parity across deposits and swaps or document this key difference.
Resolution
GMX Team: Calculation of price impact for swaps now excludes fees as well.
-
GLOBAL-11 Low Superfluous Price Impact Logic Optimization Acknowledged
Description
When computing and applying the price impact amounts in both deposits and swaps, the
elsecase handles instances where there is $0 of price impact.However, the accounting logic and corresponding event emissions are unnecessary when the
priceImpactUsdis computed to be 0.The
priceImpactUsdwill often be zero when depositing balanced long and short token amounts, depositing into homogenous markets, or making swaps that imbalance the pool as much as it is balanced.Recommendation
Exclude cases where the
priceImpactUsdis 0 from theelsebranch to avoid superfluous logic.Resolution
GMX Team: Can be optimized in a future iteration.
-
GLOBAL-12 Low Redundant Price Fetching Optimization Acknowledged
Description
The
willPositionCollateralBeSufficientfunction is invoked inIncreasePositionUtilsas well asDecreasePositionUtilswhere there is acache.collateralTokenPriceavailable in both cases.However the
willPositionCollateralBeSufficientfunction redundantly fetches the collateral token price withgetCachedTokenPrice.Recommendation
Accept the
collateralTokenPriceas a parameter for thewillPositionCollateralBeSufficientfunction to avoid fetching it again.Resolution
GMX Team: Acknowledged.
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.
