The PerpetualVault contract’s withdrawal flow in _finalize incorrectly calculates the withdrawn amount using balanceOf - prevCollateralBalance, potentially overpaying or misallocating funds if a partial liquidation occurs via liquidation after a decrease order. Specifically, prevCollateralBalance is set to 0 after a decrease order of 50,000, but the vault’s balance includes 50,000 from the decrease order and an additional i.e 10,000 from liquidation, resulting in withdrawn = 60,000. This exceeds User A’s entitled 50,000, violating fair share distribution and risking overpayment or improper fund allocation.
In the withdrawal process:
User A requests a withdrawal of 50,000 collateral from a position with 100,000 collateral, shared equally between 2 users.
A MarketDecrease order is created with collateralDeltaAmount = 50,000, reducing the position’s collateral to 0. In afterOrderExecution, prevCollateralBalance is set to 0 for the decrease order, assuming no collateral remains in the position.
The decrease order execution returns 50,000 collateral to the vault, and before _finalize is called, the position is partially liquidated by liquidation, transferring an additional 10,000 collateral to the perpVault.
Thus, collateralToken.balanceOf(address(this)) = 50,000 + 10,000 = 60,000.
In _finalize, the withdrawn amount is calculated as:
Here, collateralToken.balanceOf(address(this)) = 60,000.
prevCollateralBalance = 0 (from the decrease order).
Thus, withdrawn = 60,000 - 0 = 60,000, which is passed to _handleReturn.
In _handleReturn, the amount transferred is calculated as:
If balanceBeforeWithdrawal is interpreted as the state after the decrease order (50,000) but before liquidation, and withdrawn = 60,000, this could lead to overpayment (e.g., User A receiving 60,000 instead of their entitled 50,000) or an incorrect distribution of funds.
Root Cause:
prevCollateralBalance is set to 0 after the decrease order, but it does not account for the subsequent return of 50,000 collateral from the decrease order execution or the additional 10,000 from liquidation.
The logic in _finalize and _handleReturn does not correctly reconcile the vault’s current balance with the user’s entitled share, leading to potential overpayment or misallocation.
Overpayment Risk: Users like User A may receive more collateral (e.g., 60,000 instead of 50,000) than their entitled share, risking depletion of the vault’s funds and inequitable distribution among users.
Unfair Share Distribution: The withdrawal process violates the README’s key invariant: "Depositor Share Value Preservation: The value of a depositor’s shares should never decrease due to the actions of other depositors. Any losses or gains should be distributed proportionally based on share ownership."
Vault Insolvency Risk: Overpaying users could drain the vault’s collateral, leaving insufficient funds for other users or operations, potentially leading to insolvency.
User Trust Erosion: Incorrect withdrawals could erode trust in the protocol, potentially causing loss of users and reputational damage.
Manual code review
Modify the _finalize function to correctly account for the total collateral returned from both the decrease order and external events (e.g., liquiation) before calculating the withdrawn amount
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.
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.