When an order is filled, the user's PnL in that market is settled by fillprice(sizeDelta's corresponding BID/ASK price), not old position size's corresponding BID/ASK price(close long -> BID price, close short -> ASK price).
For simplicity, the following discussion assumes that the skew of market is equal to zero(This is not critical to the overall issue; this issue persists when skew is not 0).
For users who wish to change their existing positions, the following is the logic executed by _fillOrder
: (We omit some security checks for now)
fetch global config, perp market's config, account config, etc from storage
get funding rates for this perp market (using fillprice)
update funding rates
calculate order & settlement fees (using fillprice)
calculate required initial & maintenance margin for this trade and account's unrealized PNL (using IndexPrice)
Ensure that the user has sufficient margin (based on calculations in step 5)
cache unrealized PNL from potential existing position in this market including accrued funding (using fillprice)
update all storage and margin balance
As we can see, the contract handles unsettled PnL by closing the position at fillprice and opening a size = oldPositionSize + sizeDelta
position at fillprice.
There are two problems here:
Users with long positions can settle their PnL at ASK price by opening a tiny short position; Users with short positions can settle their PnL at BID price by opening a tiny long position.
For the market as a whole, settling PnL is the equivalent of closing a position and opening a new one. However, the closing price at this point uses the opposite BID/ASK price, turning a pricing mechanism that should be favorable to the protocol into one that is favorable to the user. This also provides room for arbitrageurs.
The actual settled PnL uses a different price than the one used for the margin test in step 5, and the account may be liquidated immediately after the order is executed, even if they pass the margin requirement in step 6.
Likelihood: high - This issue does not depend on any external assumptions and is triggered in every order.
+
Impact: high - 1. Allows users to close & open new positions in the range where the price has not been reached 2. Inconsistency with PnL checked in margin requirement.
=
Severity: high
Manual review
Unsettled PnL should be handled by using the BID/ASK price corresponding to the direction of the original position(close long -> BID price, close short -> ASK price), rather than using the BID/ASK price according to the direction of the sizeDelta
.
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.