liquidateAccounts() function inside Liquidationbranch.sol updates the open interest and skew values of the perpetual market with uninitialized values (which default to 0), compromising price calculations for that market.
liquidateAccounts() function is responsible for liquidating all market positions, whose margin collateral ratio has dropped below the healthy threshold defined by the protocol.
Reference to code: link
The function iterates through every account and then through every market position of that account and after doing all the necessary checks and actions it calls updateOpenInterest() as the final step of each nested loop.
The function itself updates two important storage variables of a perpMarket:
openInterest - stores the total open positions in the market and is used to track if the market limits have been reached
skew - the current skew of a market. This value is very important since it is used for the calculation of the market price through perpMarket.getMarkPrice() . It is a vital function used throughout the whole protocol for creating orders, filling orders, liquidations and etc.
The problem here is that when updateOpenInterest() is called inside liquidateAccounts the provided parameters - ctx.newOpenInterestX18 & ctx.newSkewX18 are both in their default (uninitialized) state, which is 0. If you go through the flow of the function you can see that both fields of the ctx struct are not set anywhere and the updateOpenInterest call is actually the first time where they get used (with their initial 0 values). Compare this with SettlementBranch._fillOrder() and you will see how those should get calculated before being passed to the update function
The final result is the reset of the openInterest & skew for the whole perpMarket when a single market position of an trading account gets liquidated which affects all other existing positions in this marker.
Because openInterest is nullified the PerpMarket.checkOpenInterestLimits() function will not track the required limits properly and all other logic depending on that variable will be distorted market wide.
Because skew is nullified, the PerpMarket.getMarkPrice() function - which is a cornerstone for most operations in Zaros - will calculate prices at significantly distorted rates, affecting almost anything in the system - order creation, settlement, liquidations etc.
Manual Review
Similar to SettlementBranch._fillOrder(), first calculate the new values of openInterest & skew and then provide them as arguments to the update function.
Since this is the liquidation function and it should not revert, the checkOpenInterestLimits() function used in SettlementBranch._fillOrder() should also be modified so that it does not revert during liquidation in case some limits are reached. You can add an additional parameter to it to conditionally check for limits when needed:
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.