# Keepers can steal additional execution fee from users

GMX keeper execution-fee accounting ignores EIP-150 1/64 gas retention, letting a keeper inflate tx.gaslimit to claim the user's entire execution fee as refund.

- Payout: Contest reward
- Severity: medium
- Program: Gmx
- Credited: J4de
- Disclosed: 2023-04-20
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2023-04-gmx-judging/issues/199
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-04-gmx%3AM-5
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:medium tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:fee-bypass

**Target:** GasUtils.payExecutionFee external library call in GMX Synthetics order execution
**Root cause:** payExecutionFee is an external call subject to EIP-150, so 1/64 of gas stays in the caller and is refunded to the keeper, but gasUsed = startingGas - gasleft() counts that retained gas as consumed.
**Steps:**
1. Keeper calls executeDeposit with tx.gaslimit chosen so startingGas is large (e.g. 6500K) while actual work is ~100K gas.
2. _executeDeposit runs, then ExecuteDepositUtils.executeDeposit calls GasUtils.payExecutionFee (external, EIP-150).
3. Inside payExecutionFee, gasUsed = startingGas - gasleft() overcounts because 1/64 gas was retained in the caller.
4. adjustGasUsage multiplies gasUsed, executionFeeForKeeper = gasUsed * tx.gasprice, capped at executionFee.
5. With startingGas=6500K, gasUsed computes to 200K, so executionFeeForKeeper equals the full 200K Gwei fee and refundFeeAmount=0.
6. Excess gaslimit is refunded to msg.sender (keeper) by the EVM, so keeper nets the full fee.
**How found:** Code review of payExecutionFee noticed it is an external function and reasoned about EIP-150's 63/64 gas forwarding rule, then modeled gasUsed with concrete numbers to show the discrepancy.
**Exploitation:** Keeper inflates tx.gaslimit on executeDeposit; the EIP-150 1/64 retention inflates computed gasUsed, making executionFeeForKeeper equal the user's full executionFee, leaving zero refund for the user while the keeper is refunded unused gas.
**Insight:** When a fee/refund formula uses startingGas - gasleft() across an external call boundary, account for EIP-150's 1/64 retention; attacker-controlled tx.gaslimit makes the overcount arbitrarily large.
