The Decrease order can output tokens instead of one , However the gamma vault only deal with one output token.
In case of Decrease order execution the GMX could output two tokens if the swap of tokens fail , see the following code:
/home/aman/Desktop/audits/2025-02-gamma/test/fuzzing/contracts/order/DecreaseOrderUtils.sol:28
28: function processOrder(BaseOrderUtils.ExecuteOrderParams memory params) external returns (EventUtils.EventLogData memory) {
29: Order.Props memory order = params.order;
30: MarketUtils.validatePositionMarket(params.contracts.dataStore, params.market);
31:
32: bytes32 positionKey = Position.getPositionKey(order.account(), order.market(), order.initialCollateralToken(), order.isLong());
33: Position.Props memory position = PositionStoreUtils.get(params.contracts.dataStore, positionKey);
34: PositionUtils.validateNonEmptyPosition(position);
35:
36: validateOracleTimestamp(
37: params.contracts.dataStore,
38: order.orderType(),
39: order.updatedAtTime(),
40: order.validFromTime(),
41: position.increasedAtTime(),
42: position.decreasedAtTime(),
43: params.minOracleTimestamp,
44: params.maxOracleTimestamp
45: );
46:
47: DecreasePositionUtils.DecreasePositionResult memory result = DecreasePositionUtils.decreasePosition(
48: PositionUtils.UpdatePositionParams(
49: params.contracts,
50: params.market,
51: order,
52: params.key,
53: position,
54: positionKey,
55: params.secondaryOrderType
56: )
57: );
58:
59:
60:
61:
62:
63:
64:
65: if (result.secondaryOutputAmount > 0) {
66: _validateOutputAmount(
67: params.contracts.oracle,
68: result.outputToken,
69: result.outputAmount,
70: result.secondaryOutputToken,
71: result.secondaryOutputAmount,
72: order.minOutputAmount()
73: );
74:
75: MarketToken(payable(order.market())).transferOut(
76: result.outputToken,
77: order.receiver(),
78: result.outputAmount,
79: order.shouldUnwrapNativeToken()
80: );
81:
82: MarketToken(payable(order.market())).transferOut(
83: result.secondaryOutputToken,
84: order.receiver(),
85: result.secondaryOutputAmount,
86: order.shouldUnwrapNativeToken()
87: );
88:
89: return getOutputEventData(
90: result.outputToken,
91: result.outputAmount,
92: result.secondaryOutputToken,
93: result.secondaryOutputAmount,
94: result.orderSizeDeltaUsd,
95: result.orderInitialCollateralDeltaAmount
96: );
97: }
98:
...
As it can be seen from the above code that the decrease order could output 2 tokens in case the swap get failed. but in gamma we only handle the single output tokens.
Check here GMX Docs
/contracts/GmxProxy.sol:260
260:
261: address outputToken;
262: uint256 outputAmount;
263: if (
264: order.numbers.orderType == Order.OrderType.MarketSwap ||
265: order.numbers.orderType == Order.OrderType.MarketDecrease
266: ) {
267: outputToken = eventData.addressItems.items[0].value;
268: outputAmount = eventData.uintItems.items[0].value;
269: }
The secondary token amount does not get handled and will result in lose for the vault and there is no way to withdraw that secondary token
One of the potential fix here would be to check that if the gmx sent the secondary token than also pass it data to PerpetualVault::afterOrderExecution
call and also handle inside _handleReturn
function.