When maintaining a position in PerpMarketV1, users are not allowed to deposit extra margin to prevent their position from being liquidated. #38
Labels
2 (Med Risk)
Assets not at direct risk, but function/availability of the protocol could be impacted or leak value
bug
Something isn't working
insufficient quality report
This report is not of sufficient quality
🤖_primary
AI based primary recommendation
sponsor disputed
Sponsor cannot duplicate the issue, or otherwise disagrees this is an issue
unsatisfactory
does not satisfy C4 submission criteria; not eligible for awards
Lines of code
https://github.com/code-423n4/2024-05-predy/blob/main/src/markets/perp/PerpMarketV1.sol#L128
Vulnerability details
Impact
When maintaining a position in PerpMarketV1, there "extra" margin is automatically withdrawed and transferred to users. This means users are not allowed to deposit extra margin to prevent their position from being liquidated.
Bug Description
During the
predyTradeAfterCallback
ofPerpMarketV1
, the required margin is calculated by_calculateInitialMargin
. If the value is negative, this means there is extra margin in the vault. However, the extra margin is always withdrawn and transferred back to users by_predyPool.take(true, callbackData.trader, uint256(-marginAmountUpdate));
.This means no extra margin is allowed in PerpMarketV1, with the lowest leverage being
leverage == 1
. Let's discuss both the long and short case:Long scenario.
leverage == 2
at priceX
. The_calculateInitialMargin()
(which returns the initial deposit amount of margin) is calculated as(netValue / leverage).toInt256() - _calculatePositionValue(vault, sqrtPrice)
, which isNX/2 - 0 == NX/2
. After deposit, thevault.margin
isNX/2
, and the liquidation price isX/2
.2X
. Now, if the user toggles with the position again, the_calculateInitialMargin()
would return2NX/2 - (NX + NX/2) == -NX/2
. TheNX
isPositionCalculator.calculateValue(sqrtPrice, PositionCalculator.getPosition(vault.openPosition))
and theNX/2
is thevault.margin
. This means an amount ofNX/2
margin is returned to the user, and this would raise the liquidation price, which is unexpected.The issue here is the position is not allowed to store extra margin, and when the price raises, margin would be withdrawn, and the liquidation price would rise.
Short scenario
leverage == 2
at price X. Similar to the long scenario, the initial deposited amount of margin would beNX/2
, and the liquidation price would be1.5X
.0.5X
. Now, if the user toggles with the position again, the_calculateInitialMargin()
would return0.5NX/2 - (0.5NX + NX/2) == -0.75NX
. The0.5NX
isPositionCalculator.calculateValue(sqrtPrice, PositionCalculator.getPosition(vault.openPosition))
and theNX/2
is thevault.margin
. This means an amount of0.75NX
margin is returned to the user, and this would lower the liquidation price, which is unexpected.Also, for the short scenario, since the minimum leverage is 1, there is no way to create a short position where the price is X and the liquidation price is above 2X (we can't create a short position with leverage smaller than 1, e.g. 0.5).
PerpMarketV1.sol
Proof of Concept
Presented above.
Tools Used
Manual review
Recommended Mitigation Steps
Add a function to manually deposit and withdraw margin from the vault (the vault should be solvent after withdraw) instead of automatically withdrawing margin.
Assessed type
Other
The text was updated successfully, but these errors were encountered: