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

Inconsistent Execution Fee Handling

Summary

While the contract checks for underpayment of execution fees via msg.value < minExecutionFee, it fails to address overpayment and relies on users accurately predicting volatile gas costs. This leads to irreversible loss of excess ETH and UX friction due to rigid fee handling.


Vulnerability Details

Key Issues

  1. No Refund for Overpayment

    • If msg.value > minExecutionFee, the excess ETH is not returned to the user.

    • Example: minExecutionFee = 0.01 ETH but user sends 0.02 ETH0.01 ETH is permanently lost.

  2. Volatile tx.gasprice Dependency

    • minExecutionFee = getExecutionGasLimit() * tx.gasprice is calculated on-chain at transaction execution time.

    • Users must guess future tx.gasprice when submitting transactions, leading to:

      • Underpayment: Transaction reverts if gas prices spike.

      • Overpayment: Excess ETH locked if gas prices drop.

  3. ETH/ERC20 Coupling

    • Users must send ETH alongside ERC20 token transfers for deposit(), creating friction:

      • Requires managing two assets (ETH + ERC20).

      • Fails if either balance is insufficient.

Affected Code

function _payExecutionFee(uint256 depositId, bool isDeposit) internal {
uint256 minExecutionFee = getExecutionGasLimit(isDeposit) * tx.gasprice;
if (msg.value < minExecutionFee) revert Error.InsufficientAmount(); // ✅ Checks underpayment
payable(address(gmxProxy)).transfer(msg.value); // ❌ No refund for overpayment
depositInfo[depositId].executionFee = msg.value;
}

Impact

  • Financial Loss: Users permanently lose overpaid ETH.

  • Transaction Failures: Unpredictable gas prices cause reverts even if users "guess" fees correctly initially.

  • Poor UX: Users must handle ETH alongside ERC20s and risk errors.


Proof of Concept (PoC)

Scenario: Overpayment Due to Gas Price Drop

  1. User Action:

    • Calls deposit(1000 USDC) when tx.gasprice = 20 gwei.

    • minExecutionFee = 200,000 gas * 20 gwei = 0.004 ETH.

    • User sends 0.005 ETH (25% overpayment).

  2. Result:

    • Transaction succeeds, but 0.001 ETH is locked in gmxProxy with no refund.

Scenario: Underpayment Due to Gas Spike

  1. User Action:

    • Estimates tx.gasprice = 10 gwei → sends 0.002 ETH.

    • At execution, tx.gasprice = 15 gweiminExecutionFee = 0.003 ETH.

  2. Result:

    • Transaction reverts → User wastes gas fees and must retry.


Recommended Mitigations

1. Refund Excess ETH

Modify _payExecutionFee to return unused ETH:

function _payExecutionFee(...) internal {
uint256 minExecutionFee = ...;
if (msg.value < minExecutionFee) revert ...;
// Refund excess ETH
uint256 excess = msg.value - minExecutionFee;
if (excess > 0) {
(bool success,) = payable(msg.sender).call{value: excess}("");
require(success, "Refund failed");
}
payable(gmxProxy).transfer(minExecutionFee);
depositInfo[depositId].executionFee = minExecutionFee; // Store actual fee used
}

2. Decouple ETH from ERC20 Operations

  • Option A: Use ERC20 for fees (e.g., stablecoin) via approval/transferFrom.

  • Option B: Allow users to pre-fund ETH into the contract (e.g., depositEth()) and deduct fees from their balance.

3. Dynamic Fee Estimation

Provide an off-chain helper to estimate minExecutionFee based on real-time gas prices:

function getEstimatedExecutionFee(bool isDeposit) public view returns (uint256) {
return getExecutionGasLimit(isDeposit) * tx.gasprice;
}
Updates

Lead Judging Commences

n0kto Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

Suppositions

There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.

Users mistake, only impacting themselves.

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelihood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

invalid_no_refund

According the sponsor, here is the design choice: - when there's gmx interaction, no refund. - when there's no gmx interaction, refund fee. The remaining fees are used for gas cost of the keepers.

Support

FAQs

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