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

Gas Price Manipulation Risk by `tx.gasprice `in GmxProxy Contract

Summary

The createOrder and settle function in the GmxProxy contract currently uses tx.gasprice to calculate execution fees. This approach introduces a security risk, as malicious users can manipulate the gas price to influence the order execution fees. This issue could lead to insufficient fees for Keeper incentives and transaction failure.

Vulnerability Details

In the current implementation of the createOrder and settle function, the execution fee is calculated as follows:

contracts/GmxProxy.sol:createOrder:#L379

contracts/GmxProxy.sol:settle#L467

function createOrder(
Order.OrderType orderType,
IGmxProxy.OrderData memory orderData
) public returns (bytes32) {
require(msg.sender == perpVault, "invalid caller");
uint256 positionExecutionFee = getExecutionGasLimit(
orderType,
orderData.callbackGasLimit
) * tx.gasprice;

The use of tx.gasprice allows users to manipulate the execution fees by setting an artificially low gas price when submitting the transaction. This could result in the following:

  1. Manipulation of Execution Fee: Users can influence the fee by adjusting the gas price, leading to an insufficient fee for executing the order.

  2. Keeper Incentive Disruption: If the calculated fee is too low, Keepers may refuse to execute the order, disrupting the system's incentive structure.

  3. Potential Transaction Failures: The actual gas price during execution may differ significantly from the transaction’s tx.gasprice, leading to failures in executing the orders.

Impact

  1. Security Risk: The ability for users to manipulate gas prices introduces a significant security flaw that can be exploited.

  2. Economic Model Disruption: The manipulation of fees undermines the economic incentives for Keepers, potentially causing orders to accumulate without being executed.

  3. Transaction Failures: Orders might fail due to gas price discrepancies between creation and execution, impacting user experience and trust in the platform.

POC
contract GmxProxyTest {
GmxProxy public gmxProxy;
function testGasPriceManipulation() public {
// 1. Create an order with a very low gas price
uint256 lowGasPrice = 1;
vm.txGasPrice(lowGasPrice);
bytes32 orderId1 = gmxProxy.createOrder(
Order.OrderType.MarketIncrease,
orderData
);
// 2. Create an order with a normal gas price
uint256 normalGasPrice = 50 gwei;
vm.txGasPrice(normalGasPrice);
bytes32 orderId2 = gmxProxy.createOrder(
Order.OrderType.MarketIncrease,
orderData
);
// 3. Verify the execution fee differences
Order.Props memory order1 = exchangeRouter.getOrder(orderId1);
Order.Props memory order2 = exchangeRouter.getOrder(orderId2);
console.log("Low Gas Price Order Fee:", order1.numbers.executionFee);
console.log("Normal Gas Price Order Fee:", order2.numbers.executionFee);
// Ensure the order with low gas price has much lower execution fee
assert(order1.numbers.executionFee < order2.numbers.executionFee / 10);
}
}

Tools Used

Manual Review

Recommendations

To prevent gas price manipulation, we recommend modifying the code to use a fixed or calculated safe gas price rather than relying on tx.gasprice. Specifically:

  1. Implement a Fixed Gas Price Mechanism: Introduce a default multiplier or a buffer based on block.basefee to calculate a more secure gas price.

  2. Use a Secure Gas Price Calculation: Implement a function to return a more consistent and predictable gas price rather than using tx.gasprice.

contract GmxProxy {
+ uint256 public constant MIN_GAS_PRICE_MULTIPLIER = 10000; // 100%
+ uint256 public constant MAX_GAS_PRICE_MULTIPLIER = 15000; // 150%
+ uint256 public gasPriceMultiplier = 12000; // Default multiplier 120%
+ function getExecutionGasPrice() public view returns (uint256) {
+ uint256 baseFee = block.basefee;
+ // Use base fee with a safe multiplier buffer
+ return (baseFee * gasPriceMultiplier) / 10000;
+ }
function createOrder(
Order.OrderType orderType,
IGmxProxy.OrderData memory orderData
) public returns (bytes32) {
require(msg.sender == perpVault, "invalid caller");
- uint256 positionExecutionFee = getExecutionGasLimit(
- orderType,
- orderData.callbackGasLimit
- ) * tx.gasprice;
+ // Use secure gas price calculation
+ uint256 gasPrice = getExecutionGasPrice();
+ uint256 positionExecutionFee = getExecutionGasLimit(
+ orderType,
+ orderData.callbackGasLimit
+ ) * gasPrice;
// ... remaining logic ...
}
}
Updates

Lead Judging Commences

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

invalid_tx-gasprice_user_manipulation

If the sender does not provide enough, the transaction to create the order won't be included in the current block: no problem. If the user provides more, they will pay more: user mistake. Moreover, the `refundFee` is set to `true` only when the keeper is the caller, preventing manipulation.

Support

FAQs

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

Give us feedback!