DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Potential Loss Due to Price Impact Exceeding Initial Collateral (`amountIn`)

Summary

In the order execution process, the price impact is calculated based on the difference between expected and actual token sizes. If the price impact in collateral tokens exceeds the initial deposit (amountIn) due to extreme market conditions (e.g., low liquidity or high volatility), the transcation will revert leading to order failures.

Vulnerability Details

The function _createIncreasePosition calculates sizeDeltaUsd using amountIn and leverage, which directly affects price impact calculations.

function _createIncreasePosition(
bool _isLong,
uint256 acceptablePrice,
MarketPrices memory prices
) internal {
//...SNIP...
Order.OrderType orderType = Order.OrderType.MarketIncrease;
collateralToken.safeTransfer(address(gmxProxy), amountIn);
>> uint256 sizeDelta = prices.shortTokenPrice.max * amountIn * leverage / BASIS_POINTS_DIVISOR;
IGmxProxy.OrderData memory orderData = IGmxProxy.OrderData({
market: market,
indexToken: indexToken,
initialCollateralToken: address(collateralToken),
swapPath: new address[](0),
isLong: _isLong,
>> sizeDeltaUsd: sizeDelta,
initialCollateralDeltaAmount: 0,
amountIn: amountIn,
callbackGasLimit: callbackGasLimit,
acceptablePrice: acceptablePrice,
minOutputAmount: 0
});
_gmxLock = true;
gmxProxy.createOrder(orderType, orderData);
}

The afterOrderExecution function calculates the amount of shares minted to the user;

function afterOrderExecution(
bytes32 requestKey,
bytes32 positionKey,
IGmxProxy.OrderResultData memory orderResultData,
MarketPrices memory prices
) external nonReentrant {
//...SNIP...
if (orderResultData.orderType == Order.OrderType.MarketIncrease) {
curPositionKey = positionKey;
if (flow == FLOW.DEPOSIT) {
uint256 amount = depositInfo[counter].amount;
uint256 feeAmount = vaultReader.getPositionFeeUsd(market, orderResultData.sizeDeltaUsd, false) / prices.shortTokenPrice.min;
uint256 prevSizeInTokens = flowData;
>> int256 priceImpact = vaultReader.getPriceImpactInCollateral(curPositionKey, orderResultData.sizeDeltaUsd, prevSizeInTokens, prices);
uint256 increased;
if (priceImpact > 0) {
>> increased = amount - feeAmount - uint256(priceImpact) - 1;
} else {
increased = amount - feeAmount + uint256(-priceImpact) - 1;
}
>> _mint(counter, increased, false, prices);
nextAction.selector = NextActionSelector.FINALIZE;

The function getPriceImpactInCollateral determines the price impact based on the difference between expected and actual token sizes.

function getPriceImpactInCollateral(
bytes32 positionKey,
uint256 sizeDeltaInUsd,
uint256 prevSizeInTokens,
MarketPrices memory prices
) external view returns (int256) {
uint256 expectedSizeInTokensDelta = sizeDeltaInUsd / prices.indexTokenPrice.min;
uint256 curSizeInTokens = getPositionSizeInTokens(positionKey);
uint256 realSizeInTokensDelta = curSizeInTokens - prevSizeInTokens;
int256 priceImpactInTokens = expectedSizeInTokensDelta.toInt256() - realSizeInTokensDelta.toInt256();
int256 priceImpactInCollateralTokens = priceImpactInTokens * prices.indexTokenPrice.min.toInt256() / prices.shortTokenPrice.min.toInt256();
return priceImpactInCollateralTokens;
}

If priceImpactInCollateral is greater than amountIn, the increased position might be unprofitable, leading to unexpected losses. This issue arises in volatile markets where price slippage significantly affects trade execution.

Scenario
Given:

  • amountIn = 1150

  • leverage = 3x

  • prices.indexTokenPrice.min = 1000e30

  • prices.shortTokenPrice.min = 0.5e30

  • prices.shortTokenPrice.max = 1e30

Step 1: Calculate sizeDeltaInUsd
sizeDeltaUsd = 1e30 * 1150 * 3 = 3.45e33

Step 2: Calculate expectedSizeInTokensDelta
expectedSizeInTokensDelta = 3.45e33/1000e30 = 3.45 ETH

Step 3: Calculate realSizeInTokensDelta
Assuming previous position size = 0.1 tokens and new size = 0.35 tokens:
realSizeInTokensDelta = 0.35 - 0.1 = 0.25 ETH

Step 4: Calculate priceImpactInTokens
priceImpactInTokens = 3.45 - 0.25 = 3.2 ETH

Step 5: Calculate priceImpactInCollateralTokens
priceImpactInCollateralTokens = 3.2 * 1000e30 / 0.5e30 = 6400 USD

Since priceImpactInCollateral (6,400) is greater than amountIn (1,150), the increased variable in afterOrderExecution becomes negative, leading to a transaction revert.

Impact

Users may lose more than their deposited amount due to excessive price impact.

Tools

Manual Review

Recommendations

Allow users to set a stop-loss to prevent excessive losses due to price impact.

Updates

Lead Judging Commences

n0kto Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Suppositions

There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.