Description:
In Gamma protocol, price impact is calculated and used only at the time when GMX calls GmxProxy::afterOrderExecution callback, which happens during the execution of MarketIncrease order, after a user deposited funds and those funds were added to the current open Gamma's position on GMX. At the time of the callback, GMX has already calculated price impact on their side and updated their state. The problem arises due to Gamma's way of calculating price impact not matching GMX's, and overall being too simple and not accounting for different possible market conditions.
This is where and how the price impact is calculated on GMX side:
https://github.com/gmx-io/gmx-synthetics/blob/b8fb11349eb59ae48a1834c239669d4ad63a38b5/contracts/pricing/PositionPricingUtils.sol#L159
https://github.com/gmx-io/gmx-synthetics/blob/b8fb11349eb59ae48a1834c239669d4ad63a38b5/contracts/pricing/PositionPricingUtils.sol#L188
https://github.com/gmx-io/gmx-synthetics/blob/b8fb11349eb59ae48a1834c239669d4ad63a38b5/contracts/pricing/PricingUtils.sol#L61
https://github.com/gmx-io/gmx-synthetics/blob/b8fb11349eb59ae48a1834c239669d4ad63a38b5/contracts/pricing/PricingUtils.sol#L88
As you can see, the calculation of price impact on GMX side is fairly complex, as it:
factors in market imbalance (long vs. short open interest differences)
includes virtual inventory checks
dynamically scales the price impact based on liquidity conditions
Here you can see the actual formula behind GMX price impact calculations:
https://github.com/gmx-io/gmx-synthetics/blob/main/README.md#price-impact
Meanwhile Gamma's way of calculating price impact looks like this:
This is a simplified version of how Gamma calculates price impact:
priceImpactInCollateralTokens = ((sizeDeltaInUsd / prices.indexTokenPrice.min) - (curSizeInTokens - prevSizeInTokens)) * prices.indexTokenPrice.min / prices.shortTokenPrice.min
Basically it takes sizeDeltaInUsd variable (which was sent in from GMX, so it's the actual value) and divides it by index token (e.g. WETH) price, so the result is in terms of WETH.
Then it takes curSizeInTokens, which is the current position size in tokens (e.g. in WETH) and subtracts it by prevSizeInTokens, which is the previous position size in tokens (before user's deposited funds were added)
Then it subracts the second result from the first result, which makes the result essentially equal to: how the position changed based on sizeDeltaInUsd minus how the position changed in terms of tokens
Finally, it applies prices to the result.
Gamma's way of calculating price impact is too simplistic and does not properly account for different possible market conditions the way GMX does.
Impact:
This issue leads to inaccuracies in share calculation for users who deposit into already open GMX positions, making them receive less or more (depending on market conditions) minted shares than users who deposited while the position was not open.
Likelihood:
This issue will occur pretty much always when a user deposits funds into Gamma's vault when there is an already open GMX position
Recommended Mitigation:
Use GMX’s non-linear price impact formula, instead of calculating price impact your own way. Make sure to factor in virtual inventory and open interest, because without these, price impact calculations will always be inaccurate.
Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.
Likelihood: High, every price impact is not accurate for short position. Impact: High, not using different formula when position is short and long lead to incorrect shares calculation. Verified with the sponsor and one of the reason why PoCs on 454 works.
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.