Summary
Improper gas estimate leads to user being wrogly billed.
Vulnerability Details
When estimating the gas limit for decrease order it wrongly estimates the amount of gas that should be supplied to gmx for its execution.
function getExecutionGasLimit(
Order.OrderType orderType,
uint256 _callbackGasLimit
) public view returns (uint256 executionGasLimit) {
uint256 baseGasLimit = dataStore.getUint(
ESTIMATED_GAS_FEE_BASE_AMOUNT_V2_1
);
uint256 oraclePriceCount = 5;
baseGasLimit +=
dataStore.getUint(ESTIMATED_GAS_FEE_PER_ORACLE_PRICE) *
oraclePriceCount;
uint256 multiplierFactor = dataStore.getUint(
ESTIMATED_GAS_FEE_MULTIPLIER_FACTOR
);
uint256 gasPerSwap = dataStore.getUint(SINGLE_SWAP_GAS_LIMIT);
uint256 estimatedGasLimit;
if (orderType == Order.OrderType.MarketDecrease) {
estimatedGasLimit =
dataStore.getUint(DECREASE_ORDER_GAS_LIMIT) +
gasPerSwap;
}
executionGasLimit =
baseGasLimit +
((estimatedGasLimit + _callbackGasLimit) * multiplierFactor) /
PRECISION;
}
While in GMX DECREASE_ORDER_GAS_LIMIT is estimated like so
function estimateExecuteDecreaseOrderGasLimit(DataStore dataStore, Order.Props memory order) internal view returns (uint256) {
uint256 gasPerSwap = dataStore.getUint(Keys.singleSwapGasLimitKey());
uint256 swapCount = order.swapPath().length;
if (order.decreasePositionSwapType() != Order.DecreasePositionSwapType.NoSwap) {
swapCount += 1;
}
return dataStore.getUint(Keys.decreaseOrderGasLimitKey()) + gasPerSwap * swapCount + order.callbackGasLimit();
}
When creating the order the decreasePositionSwapType is assigned SwapPnlTokenToCollateralToken, which means the SwapCount above will be incremented.
function createOrder(
Order.OrderType orderType,
IGmxProxy.OrderData memory orderData
) public returns (bytes32) {
require(msg.sender == perpVault, "invalid caller");
CreateOrderParams memory params = CreateOrderParams({
addresses: paramsAddresses,
numbers: paramsNumber,
orderType: orderType,
decreasePositionSwapType: Order
.DecreasePositionSwapType
.SwapPnlTokenToCollateralToken,
isLong: orderData.isLong,
shouldUnwrapNativeToken: false,
autoCancel: false,
referralCode: referralCode
});
bytes32 requestKey = gExchangeRouter.createOrder(params);
queue.requestKey = requestKey;
return requestKey;
}
Impact
Tools Used
Manual
Recommendations
Follow the same process in estimation for gas.