GMX swaps with more than one hop address will revert when GMX validates the execution fee sent from Gamma during order creation, as the fee sent will be less than what GMX expects.
In the Gamma protocol, when creating an order for a normal GMX swap, there is an array of addresses passed as a metadata parameter by the keeper called swapPath (encoded in bytes):
The swapPath can have a maximum of 2 addresses (confirmed by the sponsor), meaning its maximum length is 2.
When GmxProxy.sol is called to create the order for the swap, it first calculates the positionExecutionFee:
This will be the amount sent to GMX contracts as a native token to cover the execution cost of the order.
After sending the required data and calling the GMX contracts for the creation of the order, the GMX protocol validates that the amount sent for the execution transaction is sufficient. For that it compares the amount sent by Gamma to its own calculated executionGasLimit, the calculation is similar than to the one done by Gamma, but with a few tweaks.
The main issue arises from how GMX calculates the executionGasLimit value, which differs from Gamma’s approach and leads to discrepancies.
In GMX contracts calculation is as this:
But in Gamma protocol the executionGasLimit is calculated as follows:
As we can see, when calculating the estimatedGasLimit in Gamma, they don't account for the swapPath.length. In contrast, GMX does consider this value.
As a result, when swapPath.length is greater than 1, the positionExecutionFee calculated by GMX will always be higher than the one calculated by Gamma.
If the positionExecutionFee sent by Gamma is lower than the amount expected by GMX, the transaction will revert.
Thus, making not possible swaps with two hop addresses.
Variables extracted from GMX by keys:
swapOrderGasLimit = 2_500_000
gasPerSwap = 1_000_000
baseGasLimit = 1_647_830
estimatedGasFeePerOraclePrice = 1_936_848
multiplierFactor = 1e30
Gamma variables:
swapPath.length = 2 (parameter by the keeper)
callbackGasLimit = 2_000_000 (storage variable)
oraclePriceCount = 5 (hardcoded)
GMX variables:
swapPath.length = 2 (passed by Gamma)
callbackGasLimit = 2_000_000 (passed by Gamma)
oraclePriceCount = 3 + swapPath.length (5)
Calculation on GMX:
$ baseGasLimit = baseGasLimit + (estimatedGasFeePerOraclePrice * oraclePriceCount)$
GmxExecutionGasLimit = 17_832_070
Calculation on Gamma:
GammaExecutionGasLimit = 16_832_070
As GammaExecutionGasLimit < GmxExecutionGasLimit, swaps with more than 1 hop will always revert in the creation of the order.
The formula of estimatedGasLimit on GmxProxy.sol don't account for swapPath.length:
Gmx swaps with more than one hop address, can't be done. Breaking at some level the functionality of the protocol, hence the medium severity.
Based on GMX contracts, add the swapPath.length into the formula to calculate the estimatedGasLimit:
Likelihood: Low/Medium, when swapPath has more than 1 item. Impact: Medium/High, could lead to not enough fee collected to execute the transaction in GMX
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.