->clear logic flaw in _handleReturn()
that any savvy user can exploit to over-withdraw.
Fixing this is completely necessary to preventing share dilution and preserving fair distribution among vault participants. If you have more code or folders to share, I will continue my deep analysis and only report proven vulnerabilities.
RootCause & Where It Happens
Look at _handleReturn()
:
The problem is the else
branch, which adds withdrawn + (balanceBeforeWithdrawal * shares / totalShares)
. But withdrawn
tokens are already part of the vault’s total collateral balance at the time this function runs. By adding all of withdrawn
on top of the user’s fractional share of balanceBeforeWithdrawal
, the user effectively double-counts the newly received tokens from GMX. This inflates their withdrawal beyond their fair share.
Concrete Example Demonstrating the Theft
Initial Setup (just an example):
The vault has 300 total collateral tokens right before _handleReturn()
is called.
totalShares = 100
.
User has shares = 50
(i.e., they own half the vault).
The GMX callback just returned 100
new tokens into the vault. So now the vault’s total token balance on-chain is 300
.
How _handleReturn()
is Invoked:
In the GMX callback, the contract sets withdrawn = 100
(the newly arrived tokens) and then calls _handleReturn(100, false, false)
.
What the Code Does:
balanceBeforeWithdrawal = collateralToken.balanceOf(address(this)) - withdrawn = 300 - 100 = 200
.
amount = withdrawn + balanceBeforeWithdrawal * shares / totalShares
.
That is 100 + 200 * (50 / 100) = 100 + 100 = 200
.
Over-Withdrawal:
The user ends up getting 200 tokens even though they only owned 50% of the vault.
The vault total was 300, so the user’s correct share should have been 150.
They effectively steal an extra 50 tokens from everyone else in the vault.
WHY is it easy for any savvy user to exploit to over-withdraw
No special privileges or admin role is required. A normal user who has partial shares in an open position and triggers a withdrawal can cause _handleReturn()
to double-count.
The over-withdrawn tokens come directly at the expense of other vault participants, violating “Depositor Share Value Preservation” and “Fair Funding Fee” invariants.
The attack path is clear: just perform a partial close or partial GMX swap so that withdrawn > 0
, then rely on the flawed calculation.
Severity
High A withdrawing user can forcibly receive more than their fair share of the vault’s collateral. This results in direct, provable theft from remaining depositors.
FIX
If the vault is still open (positionClosed == false
), the user should only receive their fractional share of the vault’s entire post-transaction balance, including any newly added tokens.
One simpler approach is to unify the logic with the “position closed” path:
That means “the user always receives a fraction of the entire vault’s current collateral, equal to their fraction of total shares.”
If there is some reason to treat newly arrived tokens differently, you must ensure not to double-count them. For instance, if you want to separate “immediate proceeds from GMX” vs. “existing vault balance,” you’d need correct sub-balances, not simply add them together.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.