GMX engaged Guardian to review the security of its decentralized synthetics perpetuals exchange. From the 3rd of October to the 24th of October, a team of 3 auditors reviewed the source code in scope.
- Published
- Review window
- October 3 to 24, 2022
- Language
- Solidity
- Chains
- Arbitrum, Avalanche
- Sector
- Perpetuals
- 9 Critical
- 4 High
- 11 Medium
- 30 Low
- 0 Informational
Scope
Test suiteGuardianAudits/GMX
Overview
GMX engaged Guardian to review the security of its decentralized synthetics perpetuals exchange. From the 3rd of October to the 24th of October, a team of 3 auditors reviewed the source code in scope.
Findings 54
-
ORDU-1 Critical Limit Increase With Same Block Pricing Logical Error Resolved
Description
Proof of concept: PoC
In
validateOracleBlockNumbersForPosition, both conditionals on lines 295 and 306 handle theLimitIncreaseorder type but with different oracle block requirements.During execution, the first
ifstatement is entered, therefore aLimitIncreaseorder is forced to use prices where theoracleBlockNumbersare equal to theorderUpdatedAtBlock. This contradicts the behavior outlined in the Order.sol documentation where:“LimitSwap and LimitIncrease orders can always be executed if the right prices are reached due to this, validating that the prices presented are for blocks after the order's updatedAtBlock should be sufficient to prevent gaming of the pricing”.
Because
LimitIncreaseorders can only be executed with prices from theirupdatedAtBlockthey are rendered nearly useless.Recommendation
Remove
LimitIncreasefrom the first conditional, as it is currently duplicated.Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-1 Critical Market Used as Limit Protocol Manipulation Resolved
Description
Proof of concept: PoC
A
MarketIncreaseorder can mimic a limit order by specifying aminOutputAmountto fabricate a limit price. However,MarketIncreaseorders will always be executed at the current market price, rather than limit and stop loss orders which always execute at theacceptablePrice. This behavior allows traders to obtain an unfair advantage as they can now have a market order acting as a traditional limit order (execution price <=acceptablePrice) rather than simply using a limit order (execution price ==acceptablePrice). Combined with a stop loss, risk-free trades could be made.Consider the following scenario:
- The current price of ETH is $2,000
- User A creates a stop-loss order for ETH at $1,800
- User A creates a market order for ETH with a
minOutputAmountsuch that the market order is only executable when ETH is at or below the price of $1,800. - The price of ETH drops to $1,700
- The market increase order is then executed at $1,700 while the stop-loss order is executed at the guaranteed $1,800
- User A is able to profit $100/ETH risk free.
Recommendation
Consider cancelling the
MarketIncreaseorder if theminOutputAmountis not met. Alternatively, standardize the behavior of limit and stop loss orders so that they behave similarly to aMarketIncreaseorder with aminOutputAmount, taking the current price when it is<=theacceptablePricerather than taking theacceptablePrice. However, protocol gaming would have to be taken into account.Resolution
GMX Team:
MarketIncreaseorders are now cancelled when theminOutputAmountis not met. -
GLOBAL-2 Critical Delay Limit Success Protocol Manipulation Resolved
Description
Proof of concept: PoC1
The try/catch block in each handler does not catch any errors related to a Panic exception or custom revert. This enables a number of bugs and exploits on the exchange.
Most notably, It is possible to create a
LimitIncreaseorder that exceeds the max reserved usd, therefore reverting on execution but remaining in the order store, then when conditions are favorable the user can deposit enough reserves so their order gets executed.Consider the following scenario:
- The current price of ETH is $2,000
- User A creates a
LimitIncreaselong order for ETH at $2,000 in block 100 with usd size larger than allowable - User A’s order reverts due to insufficient reserves but remains in the order store
- 20 blocks later ether is now $2,100
- User A deposits enough reserves to execute their
LimitIncrease - The
LimitIncreaselong order now executes with the prices from block 101 - User A closes their position making risk free profit
Recommendation
Utilize
catch (bytes memory lowLevelData)to catch Panic exceptions and custom reverts.Resolution
GMX Team: The recommendation was implemented.
-
DOU-1 Critical Partial Decrease Block Not Updated Protocol Manipulation Resolved
Description
Proof of concept: PoC
Decrease orders that are partially filled are not removed from the
orderStoreand continue to exist in perpetuity. However the amount the phantom decrease position order is left with is equivalent to the amount your position was just decreased by not the amount that is leftover from the original decrease position order.Additionally, because the order is not touched upon a partial decrease order, it is possible for a user to continually submit increase orders and realize immediate and risk-free profits.
Consider the following scenario:
- The current price of ETH is $2,000
- User A creates a
MarketIncreaselong order for ETH at $2000 with $1,000,000sizeInUSD - User A’s position is opened with a size of $1,000,000, and increases to a size of $1,050,000 when ETH reaches a price of $2,100
- User A sends a
MarketDecreaseorder withsizeDeltaUSD$1,050,001 when ETH is $2,100 - User A’s position is closed but the decrease order remains in the store for $1,050,000
- The phantom
MarketDecreaseis not touched, therefore it must be executed at $2,100 - User A submits a
MarketIncreaselong order when ETH is back at $2,000 for $1,049,999 - The newly created position is closed by the
MarketDecreaseorder at $2,100 for immediate profit - The
MarketDecreaseorder continues to exist in theorderStore, and as long as the price of ETH is below the decrease order price, the user can keep submitting large longs and realizing profit
Recommendation
Do not allow partially fulfilled orders to remain in the
orderStore, or fix the partial fulfillment logic and address the block number at which these partially fulfilled orders can be executed at.Resolution
GMX Team: Partially filled orders are now touched upon execution.
-
BNK-1 Critical Bank Cannot Receive ETH Cannot Receive Ether Resolved
Description
Proof of concept: PoC
Bank.sol has no receive/fallback function defined to be able to accept ether, therefore it is impossible for the Bank to withdraw ether from the WETH contract and by extension it is impossible to transfer ether out of the Bank.sol contract.
Because of this:
- Swap orders with
hasCollateralInEth == truelead to complete loss of funds for users. - Orders, deposits, and withdrawals using
hasCollateralInEth == trueare unable to be canceled. This means users are unable to cancelhasCollateralInEthorders and the keeper is unable to cancel failing orders, deposits, and withdrawals leading to phantom entries in each corresponding store. StopLossorders with noswapPathandhasCollateralInEth == truecannot be executed as they attempt to pay the user in ether from the bank.- Additionally, users are unable to use the
hasCollateralInEthflag to receive ether when withdrawing or exercising a decrease order.
Recommendation
Implement a receive function in the Bank.sol contract.
Resolution
GMX Team: The recommendation was implemented.
- Swap orders with
-
DOU-2 Critical Loss of Funds on Swap Path Logical Error Resolved
Description
Proof of concept: PoC
For decrease orders with a defined
swapPaththeinputAmountfor the swap is theinitialCollateralDeltaAmount, which cannot be set for decrease order types and defaults to 0.Therefore, if a user supplies a
swapPathon a decrease order, they will experience a complete loss of funds. Zero tokens would be swapped and sent back to the user, yet their position would still decrease/close.Recommendation
Provide the correct
inputAmountfor the swap that corresponds to how much the user’s position was decreased by.Resolution
GMX Team: The recommendation was implemented.
-
MKTU-1 Critical Pool Value With Inverse PnL Logical Error Resolved
Description
Proof of concept: PoC
The pool value interprets traders profiting as an increase of value for the pool, and traders losing as a decrease of value of the pool. This is exactly the inverse of what should be happening. When traders lose and their loss is realized, their collateral is taken into the pool and the value of the pool increases.
Recommendation
Use
return Calc.sum(value, -pnl).Resolution
GMX Team: The recommendation was implemented.
-
LIQH-1 Critical Uninitialized Order Store Missing Variable Resolved
Description
The liquidation handler has no reference to the
orderStore, and does not assign one in the params toprocessLiquidation. Therefore, liquidations revert when attempting to access theorderstoreon theExecuteOrderParamsparams. Therefore it is impossible to perform liquidations on any position.Recommendation
Include the
orderStorein theExecuteOrderParamsin theliquidatePositionfunction.Resolution
GMX Team: The recommendation was implemented.
-
LIQH-2 Critical Incorrect Blocks For Liquidation Logical Error Resolved
Description
In
processLiquidation, themarketDecreaseorder that is created for liquidation does not get touched, therefore theupdatedAtBlockis always 0.Therefore it is impossible to execute a liquidation with the correct block number for oracle prices and by extension impossible to liquidate any position.
Furthermore, the
oracleBlockNumberrequirements for aMarketDecreaseorder stipulate that the prices come from the block in which the order was updated at. This would demand that the oracle provide finalized prices for the block in which the liquidation transaction is to be executed, which is impossible as the block has not yet been confirmed.Recommendation
Resolve the contradiction in block numbers for liquidations and
MarketDecreaseorders. Additionally, make sure to calltouch()on the order when creating it in theprocessLiquidationfunction.Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-3 High Cannot Withdraw Backed Position Logical Error Resolved
Description
Proof of concept: PoC
If a user creates a long position with the short token as collateral, they may only withdraw the long token or short token if there is enough short token liquidity in the pool. This can lead to circumstances where users are unable to withdraw from their positions even though they are technically backed.
This is because positions are keyed based on the
initialCollateralTokenand decrease orders must specify the sameinitialCollateralTokenso there must exist enough short token liquidity to pay out the position for a decrease order.This is strange as long positions are backed by long tokens, thus the user should be able to withdraw the long token upon decrease without relying on adequate short token liquidity to perform a swap.
Recommendation
Allow users to withdraw the long token in this case, or prevent such a scenario from happening in the first place in the
validateReservesfunction.Resolution
GMX Team: Users profits are now paid in their backing PnL token.
-
ORDH-1 High Waste Keeper Gas Protocol Manipulation Resolved
Description
Proof of concept: PoC
An order’s
executionFeeis validated when the keeper is already attempting to execute the order. A malicious user can send a large number of orders that are unable to be executed and have anexecutionFeeof 0.This will waste the keeper’s gas at no cost to the user each time execution is attempted, potentially delaying the execution of other deposits, withdrawals, and orders on the exchange.
Recommendation
Validate the
executionFeeupon order creation, similarly to deposits.Resolution
GMX Team: The recommendation was implemented.
-
FR-1 High Cannot Withdraw Fees Trapped Funds Resolved
Description
The
FeeReceivercontract is responsible for receiving fees for the entire protocol, but provides no functions to retrieve erc20 tokens nor ether. Therefore any fees sent to theFeeReceivercontract are lost.Recommendation
Add a withdraw function with proper access controls to withdraw and manage fees.
Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-4 High Arbitrage Attack Protocol Manipulation Acknowledged
Description
Documentation in README.md suggests that spot prices from multiple exchanges will be used to determine prices for execution. Such a price collection scheme potentially allows for economically viable price manipulation/arbitrage attacks. Attackers may be able to manipulate prices to game orders into guaranteed profits, or cause mass liquidations.
Recommendation
Adopt a TWAP with multiple price readings to make such attacks economically unviable. Otherwise, be prepared to use failsafes such as open interest caps to limit these attacks.
Optionally, order book depth/liquidity for each exchange should be considered in the calculation to further limit the scope of manipulation attacks.
Resolution
GMX Team: Acknowledged
-
GLOBAL-5 Medium Centralization Risk Centralization / Privilege Acknowledged
Description
The exchange keeper and other permissioned addresses have the power to do nearly anything:
- Execute any order or liquidation at any arbitrary price
- Defer execution of any arbitrary deposit, withdrawal, order, or liquidation
- Select the order of execution for all incoming deposits, withdrawals, and orders
- Power to shut off any feature – including withdrawals and closing positions
- Change out the protocol-wide variables used in the
dataStore
There are many assumptions made on the form of the price input from the keeper. Adding validation for the expected format and range of acceptable inputs would reduce the risk of high-cost mistakes, and assist in limiting the scope of the internal exploits available to the keeper.
The keeper must diligently execute orders, for example stop loss orders must be executed with the correct range of block numbers while staying within the
MAX_ORACLE_BLOCK_AGElimit.Recommendation
Treat the keeper’s private key(s) with the utmost level of security and introduce as many safeguard checks as possible to limit the scope of the keepers potential attack vectors.
Resolution
GMX Team: Acknowledged
-
OCL-1 Medium Potential Gas DoS Denial-of-Service Acknowledged
Description
There is a potential DoS with the
setPricesfunction, as the lengths of thetokens,signers,compactedOracleBlockNumbers, andcompactedPricesarrays are unbounded. Theforloop through these arrays in addition to memory expansion costs may result in gas fees exceeding the block gas limit, especially with long swap routes that require many token prices.Recommendation
Carefully consider the max signer bounds to limit gas usage. Furthermore, consider bounds on the number of tokens, as all signers are iterated over for each token.
Resolution
GMX Team: The sizes of these arrays in practice should not come close to the block gas limit.
-
FR-2 Medium Lack of Access Control For Events Access Control Acknowledged
Description
Anyone can call
notifyFeeReceivedas there is a lack of access control, which leads to a depreciation of the authenticity of the event.Recommendation
Implement access control concerning who may call
notifyFeeReceived.Resolution
GMX Team: Acknowledged.
-
DEPU-1 Medium Execution Fee DoS Denial-of-Service Acknowledged
Description
There is a potential DoS with
createDeposit, as a malicious address may send a miniscule amount of WETH to the deposit store such that another user’s WETH deposit no longer matches theexecutionFeein their deposit. Thus, the deposit execution reverts and is unable to be created.Recommendation
Consider removing the strict equality for the
executionFeeor handling the accounting in thedepositStoresuch that another address cannot increase the deposit amount for a user.Resolution
GMX Team: Acknowledged.
-
ERTR-1 Medium Missing Validation Input Validation Acknowledged
Description
When creating a deposit, there is no validation that the
longTokenor theshortTokenare valid for the supplied market. If WBTC is accidentally used as the long token in an ETH/USDC market, the user would lose their WBTC in thedepositStore.Furthermore, there is no validation that either the long token amount or short token amount is non-zero. This check should be added to prevent the keeper from executing trivial deposits.
Additionally, when creating an order, there is no validation that the initial collateral token is valid for the provided market, which can lead to invalid orders stored in the
orderStore.Recommendation
Implement the above mentioned validations.
Resolution
GMX Team: Acknowledged
-
OCL-2 Medium Chainlink Feed Validation Pricefeed Validation Acknowledged
Description
Extra validation checks should be added on the result from the Chainlink price feed to ensure non-stale data. The price from the data feed influences the execution of orders and liquidations so it is imperative the data is up to date and correct.
Recommendation
Add the following require statements to validate the price feed:
require(answeredInRound >= roundID, "Chainlink:: Stale price")require(timestamp > 0, "Chainlink:: Round not complete")
Resolution
GMX Team: Acknowledged.
-
ORDH-2 Medium Phantom Decrease Logical Error Acknowledged
Description
It is possible to have a decrease order in the
orderStorewhen no corresponding position is present because orders are not cancelled upon failure with theEMPTY_POSITION_ERROR_KEY.Consider the following scenario:
- User A creates a position
- User A sends a
LimitDecreaseOrderand aMarketDecreaseOrderin the same block LimitDecreaseOrdergets executed firstMarketDecreaseOrderis now floating on an empty position and it never gets canceled- User A opens a new position with an increase order of the same market, collateral token, and directionality
- The market decrease order unexpectedly affects User A’s new position as it was waiting to be executed in the
orderStore
Recommendation
Cancel the order upon failed execution in the case of
EMPTY_POSITION_ERROR_KEY.Resolution
GMX Team: Acknowledged.
-
OCL-3 Medium Block Number Validation Input Validation Acknowledged
Description
According to documentation, the price for a token is only retrieved once a block is finalized.
However,
_setPricesdoes not revert when the block number of the prices provided by the oracle equals the current block number:if (cache.oracleBlockNumber > block.number) { revert … }.This allows the keeper to provide prices for a block number that is equivalent to the current block and waste gas when it would likely continue on to revert when validating signatures.
Recommendation
Use
>=instead of>.Resolution
GMX Team: Acknowledged
-
DPU-1 Medium Unable to Decrease Collateral Logical Error Resolved
Description
In the
processCollateralfunction, thecollateralDeltaAmountis initially set toparams.order.initialCollateralDeltaAmount().toInt256(), which cannot be set for decrease orders. Therefore, there is no way to decrease collateral from a position without closing the entire position.Recommendation
Allow for
initialCollateralDeltaAmountto be set for decrease orders so users are able to decrease their collateral without closing their position.Resolution
GMX Team: The recommendation was implemented.
-
POSU-1 Medium Cannot Liquidate 0 Remaining Collateral Logical Error Resolved
Description
There is potential for a position with $0 of
remainingCollateralto avoid liquidation. This is due to the fact that the first conditional can avoid being triggered if theMIN_COLLATERAL_USDwas not set. If so, the second conditional would revert due to division by 0. Thus, a position that is liquidatable cannot be liquidated.Recommendation
Add zero checks for such a scenario.
if (remainingCollateralUsd == 0) return true;Resolution
GMX Team: The recommendation was implemented.
-
MKTF-1 Medium Same Short and Long Token Input Validation Resolved
Description
When creating a new market, nothing prevents the long token from being set to the same address as the short token.
In such a case, deposits would double count the funds you are sending to the pool. Since the creation of markets will eventually be unpermissioned, this validation is paramount.
Recommendation
Validate that the long token is not equal to the short token.
Resolution
GMX Team: The recommendation was implemented.
-
ORD-1 Low Typo Typo Acknowledged
Description
On line 58, “current” is misspelled as “curent“.
Recommendation
Replace “curent” with “current” in the comment.
Resolution
GMX Team: Acknowledged.
-
OCL-4 Low Jagged Set of Arrays Input Validation Acknowledged
Description
In the
_setPricesfunction, nothing prevents thesignaturesarray from being a different length than thesignersarray. If there are less signatures than signers, that would lead to an out-of-bounds error and the index price would not be able to get set.In this case it would save gas to add a length validation check before commencing potentially expensive price computations.
Recommendation
Check that the length of the
signaturesarray is a multiple of the length of thesignersarray.Resolution
GMX Team: The net result of the recommendation is still a reverted transaction, however optimizing for the revert case rather than the expected use case.
-
GOV-1 Low Pull Over Push Ownership Ownership / Privilege Acknowledged
Description
The ownership transfer process should have a push and pull step rather than executing in a single transaction with
setGov.This way the protocol may avoid catastrophic errors such as setting the wrong governance address.
Recommendation
Implement a two step transfer process for the
govrole where the newgovaddress must explicitly accept its new role.Resolution
GMX Team: Acknowledged.
-
GOV-2 Low Zero Address Checks Best Practices Acknowledged
Description
In the
setGovfunction there are no zero address checks on the newgovaddress.Recommendation
Add zero address checks as an errantly set
govaddress would be catastrophic for the protocol.Resolution
GMX Team: Acknowledged.
-
ERTR-2 Low Zero Address Checks Best Practices Resolved
Description
The
createDeposit,createWithdrawal, andcreateOrderfunctions compose the user’s interface for the exchange but perform no zero address checks on the addresses they accept.Recommendation
Add zero address checks to avoid any unexpected behavior when users interact with these functions. Document where zero addresses are expected, and enforce when they are not.
Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-6 Low Immutability Modifiers Mutability Resolved
Description
Throughout the contracts many contract level variables can be declared
immutable. Common variables such as thedataStore,depositHandler,withdrawalHandler,orderHandler,depositStore,withdrawalStore, andorderStoreamong others should be declaredimmutablein multiple contracts.Recommendation
Implement the above recommended immutability modifiers.
Resolution
GMX Team: The recommendation was implemented.
-
ORDU-2 Low Unused Custom Revert Superfluous Code Resolved
Description
In the
setExactOrderPricefunction, the revert statement on line 264 can be replaced with arevertUnsupportedOrderTypefunction call.Recommendation
Implement the above function call.
Resolution
GMX Team: The recommendation was implemented.
-
DEPH-2 Low Visibility Modifiers Visibility Resolved
Description
The
_executeDepositfunction is called from within the context of a try/catch, therefore it can be declaredexternal.Recommendation
Declare the function
external.Resolution
GMX Team: The recommendation was implemented.
-
ORDH-3 Low Visibility Modifiers Visibility Resolved
Description
The
_executeOrderfunction is called from within the context of a try/catch, therefore it can be declaredexternal.Recommendation
Declare the function
external.Resolution
GMX Team: The recommendation was implemented.
-
SWU-1 Low Redeclared Variables Optimization Acknowledged
Description
The
market,nextIndex,receiver, and_paramsvariables can be declared outside of theforloop to save gas.Recommendation
Declare these variables outside the
forloop.Resolution
GMX Team: Acknowledged.
-
GLOBAL-8 Low Unclear Naming Readability Resolved
Description
hasCollateralInEthis used in many different ways throughout the codebase, it should have a more adaptable/explicit name.Recommendation
Rename
hasCollateralInEthor assign specific names to use-cases such as deposits and withdrawals.Resolution
GMX Team:
hasCollateralInEthis nowshouldUnwrapNativeToken. -
DPU-2 Low Redundant Parameters Superfluous Code Acknowledged
Description
The
processCollateralfunction accepts apositionparameter, but the same position is already available on theparamsparameter of typeDecreasePositionParams.Recommendation
Remove the
positionparameter as it is available on theDecreasePositionParams.Resolution
GMX Team: Acknowledged.
-
GLOBAL-9 Low Flexible Withdrawals Unique Behavior Acknowledged
Description
It should be noted that when withdrawing
marketTokens, a user will not always be able to redeem the corresponding amounts of underlying assets that they provided. This is because other depositors may withdraw whichever long or short collateral token they wish, as long as their withdrawal matches themarketTokenvalue they received for depositing into the market.Example:
- The ETH/USD market is empty
- User A deposits $500 worth of ETH and $500 worth of USDC
- User B deposits $500 worth of ETH and $500 worth of USDC
- User A withdraws $1000 worth of USDC
- User B attempts to withdraw $500 worth of ETH and $500 worth of USDC, but the tx reverts because there is no USDC left in the pool
Recommendation
Ensure the withdrawal behavior is clearly documented and communicated to users.
Resolution
GMX Team: This will be clearly documented for users.
-
ORDH-4 Low Silent Cancelation Events Resolved
Description
When an order is executed and reverts with an error that is not retried, it gets canceled silently. There are no events emitted nor is there a return value for the
executeOrderfunction to communicate whether an order was canceled or not.Recommendation
Implement a return value or event that indicates whether or not an order was canceled.
Resolution
GMX Team: An event is now emitted upon order cancellation.
-
BNK-2 Low Duplicate Validation Checks Superfluous Code Acknowledged
Description
In Bank.sol the
require(receiver != address(this), "Bank: invalid receiver")check can be deduplicated into a modifier that also validates that the receiver is a non-zero address.Additionally, these checks can be made in
transferOutand validate that the amount is greater than zero.Recommendation
Implement the above refactors.
Resolution
GMX Team: Acknowledged.
-
GLOBAL-10 Low Lack Of Events Events Resolved
Description
Throughout the contracts there are missing events for important actions such as order execution, position size modifications, liquidations, deposits, and withdrawals.
Recommendation
Implement events throughout the contracts for improved monitoring, readability, and transparency.
Resolution
GMX Team: Events have been added throughout the contracts.
-
POSS-1 Low Visibility Modifiers Visibility Resolved
Description
The function
containsis declared aspublicbut is never called from within the contract.Recommendation
Modify the visibility from
publictoexternal.Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-11 Low Storage Location Optimization Acknowledged
Description
The following parameters are marked as
memorywhen they could be declaredcalldata:- PositionStore::18
- DepositHandler::73,102
- OrderHandler::58,73,155
- OrderUtils::73
- DepositUtils::61
- DepositStore::17
- WithdrawalUtils::74
- WithdrawalStore::17
- WithdrawalHandler::84,112
- OracleModule::15
- Oracle::82
- LiquidationHandler::51
Recommendation
If it is desired to reduce gas for these functions, potentially at the expense of deployment costs, change the suggested memory modifiers from
memorytocalldata.Resolution
GMX Team: Acknowledged.
-
GLOBAL-12 Low Default Value Assignment Optimization Acknowledged
Description
Throughout the contracts
uintvariables are initialized to the default value (0):- MarketUtils.sol::619
- Oracle.sol::100,136,226,252,285
- OracleUtils.sol::45
- SwapUtils.sol::44
- Timelock.sol::38,44
- Array.sol::33,41,51
Recommendation
Remove the unnecessary assignments.
Resolution
GMX Team: Acknowledged.
-
GLOBAL-13 Low Array Length Computations Optimization Acknowledged
Description
Throughout the contracts array lengths should be computed outside of
forloops:- MarketUtils.sol::619
- Oracle.sol::22,100,252,285
- SwapUtils.sol::44
- Timelock.sol::38,44
- Array.sol::33,41,51
Recommendation
Compute array lengths outside of
forloops.Resolution
GMX Team: Acknowledged.
-
GLOBAL-14 Low Shorten Revert Strings Optimization Acknowledged
Description
Throughout the contract, revert strings can be shortened to 32 bytes to decrease gas costs:
- WithdrawalHandler.sol::46
- IncreaseOrderUtils.sol::48
- OrderUtils.sol::264
- DecreasePositionUtils.sol::141
- WithdrawalUtils.sol::76,106,204
Recommendation
Shorten revert strings to 32 bytes.
Resolution
GMX Team: Acknowledged.
-
GLOBAL-15 Low Division/Multiplication Optimization Optimization Acknowledged
Description
Throughout the contract, switching from division/multiplication to right-shift/left-shift can save gas:
- PricingUtils.sol::64
- Array.sol::62,65
Recommendation
Switch uses of division to right-shift and uses of multiplication to left-shift.
Resolution
GMX Team: Acknowledged.
-
DEPU-2 Low Recalculating Stored Values Optimization Acknowledged
Description
In the call to
SwapPricingUtils.GetSwapPricingParams, the values stored inlongTokenUsdandshortTokenUsdon lines 113 and 114 are recalculated on lines 126 and 127.Recommendation
Use the previously computed values instead of recalculating.
Resolution
GMX Team: Acknowledged.
-
ORDU-3 Low Unnecessary Variable Optimization Acknowledged
Description
Declaring the
pricevariable on line 208 is unnecessary, as the calculated value is only used once.Recommendation
Remove the
pricevariable declaration and use the result of the calculation directly.Resolution
GMX Team: Acknowledged.
-
IOU-1 Low Validation Optimization Optimization Resolved
Description
Validation for the
marketcan occur before any transfer logic in order to save gas.Recommendation
Move the
MarketUtils.validateNonEmptyMarketcall to the first line inprocessOrder.Resolution
GMX Team: The recommendation was implemented.
-
GLOBAL-16 Low Custom Reverts Optimization Resolved
Description
Throughout the contracts, several
requirestatements can be converted to custom reverts. The following files contain statements that can be replaced:- OrderUtils.sol::90
- WithdrawalUtils::76,204
- DepositHandler::44
- OrderHandler::53,130
- withdrawalHandler::46
- RoleModule.sol::31,36,41,46,51
Recommendation
Replace
requirestatements with custom reverts.Resolution
GMX Team: The recommendation was implemented.
-
WTDU-1 Low Unnecessary Validation Optimization Acknowledged
Description
MarketToken.burnwill revert on its own if the user has insufficient market tokens.Recommendation
Remove the validation check if a custom revert is not necessary.
Resolution
GMX Team: Acknowledged.
-
DPU-3 Low Unnecessary Variable Set Superfluous Code Acknowledged
Description
There is no need to set the position’s
collateralAmountto 0, as the position is immediately removed on line 99.Recommendation
Remove the line setting the position’s
collateralAmount.Resolution
GMX Team: Acknowledged.
-
GLOBAL-17 Low For-Loop Increment Optimization Acknowledged
Description
Throughout the contracts numerous
forloops have upper bounds with no risk of overflow for the index. Therefore an unchecked block can be used in the loop to increment the index and save gas.Recommendation
Implement the above suggestion.
Resolution
GMX Team: Acknowledged.
-
GLOBAL-18 Low Visibility Modifiers Optimization Acknowledged
Description
Throughout the codebase there are numerous library functions that can be declared
internalto save gas.Recommendation
Declare library functions that do not need to be called in an
externalcontext asinternal.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.
