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

Insufficient Price Deviation Controls in GmxProxy Orders

Summary

The GmxProxy contract lacks additional price deviation controls beyond GMX's native protections. While GMX implements its own price impact limits and oracle validations, the absence of protocol-level checks in GmxProxy creates potential opportunities for price manipulation within GMX's allowed thresholds.

Vulnerability Details

GmxProxy forwards orders to GMX without implementing its own price deviation checks:

function createOrder(
Order.OrderType orderType,
IGmxProxy.OrderData memory orderData
) public returns (bytes32) {
require(msg.sender == perpVault, "invalid caller");
CreateOrderParamsNumbers memory paramsNumber = CreateOrderParamsNumbers({
sizeDeltaUsd: orderData.sizeDeltaUsd,
initialCollateralDeltaAmount: orderData.initialCollateralDeltaAmount,
triggerPrice: 0,
acceptablePrice: orderData.acceptablePrice, // @audit Relies solely on GMX protections
executionFee: positionExecutionFee,
callbackGasLimit: orderData.callbackGasLimit,
minOutputAmount: orderData.minOutputAmount,
validFromTime: 0
});
bytes32 requestKey = gExchangeRouter.createOrder(params);
queue.requestKey = requestKey;
return requestKey;
}

Key considerations:

  1. GMX provides base layer protections:

    • Price impact limits

    • Delay blocks

    • Oracle validations

    • Position collateral checks

  2. However, GmxProxy could benefit from additional protocol-specific controls:

    • Protocol-level price deviation limits

    • Additional checks based on protocol's risk parameters

    • Custom validation logic for specific order types

Impact

The potential impact is moderate because:

  1. Limited by GMX's native protections:

    • Price impact caps restrict extreme manipulations

    • Oracle validations prevent major deviations

    • Position collateral requirements limit risk exposure

  2. Still poses risks:

    • Price manipulation within GMX's allowed thresholds

    • MEV opportunities in specific market conditions

    • Higher slippage for users in volatile markets

Proof of Concept

The following PoC demonstrates potential price manipulation within GMX's limits:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import {Test, console2} from "forge-std/Test.sol";
import {GmxProxy} from "../../contracts/GmxProxy.sol";
import {IGmxProxy} from "../../contracts/interfaces/IGmxProxy.sol";
import {Order} from "../../contracts/libraries/Order.sol";
contract GmxProxyPriceDeviationTest is Test {
GmxProxy public proxy;
address public perpVault;
address public user;
// Mock contracts
MockMarket public market;
MockPriceOracle public priceOracle;
function setUp() public {
// Setup contracts and mock environment
market = new MockMarket();
priceOracle = new MockPriceOracle();
proxy = new GmxProxy();
proxy.initialize(
address(0x1), // orderHandler
address(0x2), // liquidationHandler
address(0x3), // adlHandler
address(market), // gExchangeRouter
address(0x5), // gmxRouter
address(0x6), // dataStore
address(0x7), // orderVault
address(0x8), // gmxReader
address(0x9) // referralStorage
);
perpVault = address(0xA);
user = address(0xB);
// Fund accounts
vm.deal(user, 100 ether);
}
function testPriceDeviation() public {
// Setup initial market conditions
uint256 initialPrice = 1500e8; // ETH at $1500
priceOracle.setPrice(initialPrice);
// Create order within GMX limits but with potential for manipulation
vm.startPrank(perpVault);
IGmxProxy.OrderData memory orderData = IGmxProxy.OrderData({
market: address(market),
indexToken: address(0x1),
initialCollateralToken: address(0x2),
swapPath: new address[](0),
isLong: true,
sizeDeltaUsd: 10e18, // $10 position size
initialCollateralDeltaAmount: 1e18, // 1 ETH collateral
amountIn: 1e18,
callbackGasLimit: 400000,
// Price deviation within GMX limits but above optimal
acceptablePrice: initialPrice * 11 / 10, // 10% slippage
minOutputAmount: 0
});
bytes32 orderId = proxy.createOrder(Order.OrderType.MarketIncrease, orderData);
vm.stopPrank();
// Simulate price movement within GMX limits
market.setPrice(initialPrice * 105 / 100); // 5% price increase
// Execute order
vm.prank(address(market));
proxy.afterOrderExecution(
orderId,
createMockOrder(),
createMockEventData()
);
// Verify suboptimal execution price
assertEq(
market.getLastExecutionPrice(),
initialPrice * 105 / 100,
"Order executed at manipulated price within GMX limits"
);
}
}
// Mock contracts
contract MockMarket {
uint256 public price;
uint256 public lastExecutionPrice;
function setPrice(uint256 _price) external {
price = _price;
}
function getLastExecutionPrice() external view returns (uint256) {
return lastExecutionPrice;
}
}
contract MockPriceOracle {
uint256 public price;
function setPrice(uint256 _price) external {
price = _price;
}
}

This PoC demonstrates:

  1. Order creation with suboptimal but GMX-acceptable price deviations

  2. Price manipulation within GMX's allowed thresholds

  3. Impact on trade execution quality

Tools Used

  • Manual code review

  • Foundry testing framework

  • GMX documentation analysis

Recommendations

Add protocol-specific price deviation controls:

contract GmxProxy {
uint256 public constant PROTOCOL_MAX_PRICE_DEVIATION_BPS = 50; // 0.5%
function createOrder(
Order.OrderType orderType,
IGmxProxy.OrderData memory orderData
) public returns (bytes32) {
require(msg.sender == perpVault, "invalid caller");
// Get current market price
MarketPrices memory prices = getMarketPrices(orderData.market);
uint256 currentPrice = orderData.isLong ?
prices.indexTokenPrice.max :
prices.indexTokenPrice.min;
// Protocol-specific price deviation check
uint256 maxDeviation = (currentPrice * PROTOCOL_MAX_PRICE_DEVIATION_BPS) / 10000;
if (orderData.isLong) {
require(
orderData.acceptablePrice <= currentPrice + maxDeviation,
"Protocol price deviation too high"
);
} else {
require(
orderData.acceptablePrice >= currentPrice - maxDeviation,
"Protocol price deviation too high"
);
}
// Continue with order creation
CreateOrderParamsNumbers memory paramsNumber = CreateOrderParamsNumbers({
// ... existing parameters ...
});
bytes32 requestKey = gExchangeRouter.createOrder(params);
queue.requestKey = requestKey;
return requestKey;
}
}

These changes:

  1. Add protocol-level price deviation controls

  2. Complement GMX's existing protections

  3. Improve trade execution quality

  4. Reduce MEV opportunities

Risk Level Analysis

This issue is classified as Medium severity because:

  1. Impact is limited by GMX's protections:

    • Price impact caps

    • Oracle validations

    • Position collateral requirements

  2. Likelihood is moderate:

    • Requires specific market conditions

    • Limited by GMX's own security measures

    • Economically viable only in certain scenarios

  3. Not High severity because:

    • Base GMX protections mitigate catastrophic scenarios

    • No direct fund loss risk

    • Manipulation requires significant capital

  4. Not Low severity because:

    • Real economic impact on users

    • Consistent MEV opportunities

    • Affects protocol's core trading functionality

Additional Context

The recommended solution complements GMX's existing protections rather than replacing them. The goal is to:

  1. Add protocol-specific safeguards

  2. Improve user trade execution

  3. Maintain compatibility with GMX's architecture

  4. Reduce potential for price manipulation within allowed thresholds

The severity assessment considers both the existing GMX protections and the potential for improvement through protocol-level controls.

Updates

Lead Judging Commences

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

invalid_gmx_increase/decrease_no_slippage

acceptablePrice does that job for increase/decrease positions. https://github.com/gmx-io/gmx-synthetics/blob/caf3dd8b51ad9ad27b0a399f668e3016fd2c14df/contracts/order/BaseOrderUtils.sol#L276C49-L276C66

Support

FAQs

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