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

Improper Position Closure During Withdrawal Leads to Loss of Funds for Non-Withdrawing Depositors

Code Areas

Summary

When a single user requests a withdrawal, the vault may be forced to close the entire GMX position if maintaining a partial position becomes impossible. In such cases, all withdrawn collateral is incorrectly sent to the withdrawing user, resulting in a complete loss of funds for other depositors in the vault.

Vulnerability Details

The vulnerability occurs in the withdrawal process when willPositionCollateralBeInsufficient is triggered. This can happen due to:

When triggered, the vault closes the entire position by setting sizeDeltaInUsd to the full position size:

if (willPositionCollateralBeInsufficient(...)) {
sizeDeltaInUsd = sizeInUsd; // Close entire position
}

However, the withdrawal logic incorrectly assumes that only the withdrawing user's portion should be processed, leading to a critical accounting error where all withdrawn funds are sent to a single user.

Issue Two

Another related issue is that under such market conditions, any user can force the rest of the traders out of the market, giving them zero market exposure.

Proof of Concept

Consider this scenario with 3 users who each deposited 100 tokens:

  1. Total position size = 300 tokens

  2. User A requests withdrawal of their 100 tokens (1/3 of position)

  3. Due to GMX constraints, the entire position must be closed

  4. All 300 tokens are withdrawn to the vault

  5. The withdrawal process executes:

function _finalize(bytes memory data) internal {
if (flow == FLOW.WITHDRAW) {
// withdrawn = 300 (full position closure amount)
uint256 withdrawn = collateralToken.balanceOf(address(this)) - prevCollateralBalance;
_handleReturn(withdrawn, positionClosed, refundFee);
}
}
function _handleReturn(uint256 withdrawn, bool positionClosed, bool refundFee) internal {
// User A has 1/3 shares but receives all 300 tokens
amount = withdrawn + balanceBeforeWithdrawal * shares / totalShares;
_transferToken(depositId, amount); // Transfers all 300 tokens to User A
_burn(depositId); // Burns only User A's shares
}
  1. Result:

    • User A receives 300 tokens

    • Users B and C lose their 200 tokens despite still having valid shares

Impact Details

The vulnerability allows the first withdrawing user to receive the entire position's collateral, causing a complete loss of funds for all other depositors in the vault. This breaks the fundamental principle of proportional share distribution and represents a critical security issue since any user could potentially drain the entire vault during their withdrawal, regardless of their actual share percentage.

Tools Used

Manual Review

Recommendations

For simplicity and a solution that fixes both issues:

  • Add a mechanism that prevents the withdrawal of tokens if it's going to lead to the closure of the position. This prevents a griefer from forcing the rest of the traders out of the market.

  • Add a rebalancing mechanism that adjusts the risk exposure of the vault's position, such that any user can redeem their shares when needed. This avoids forcing users to stay in the market against their will, and protects the non-withdrawers from losing their tokens during other user's withdraw events.

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.

Support

FAQs

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