DeFiFoundry
60,000 USDC
View results
Submission Details
Severity: high
Valid

Liquidation is zeroing the skew and open interest

Summary

After the private audit and fixing [L-03] the following changes introduced made the open interest and skew to be set to 0 after each liquidation, which will lead to confusion that the market is balanced but in reality it won’t be:

- (ctx.newOpenInterestX18, ctx.newSkewX18) = perpMarket.checkOpenInterestLimits(
- ctx.liquidationSizeX18, ctx.oldPositionSizeX18, sd59x18(0), false
- );

Vulnerability Details

The intention of skew is so critical for a Market since it is used to balance the ratio of long/short positions in the system, by indicating what fees should be paid, calculating the price impact of order and calculating the current funding velocity.

After the fix from the previous audit newOpenInterestX18 and newSkewX18 are left unmodified and they will be their initial values - 0, this will reset the skew and OI after each liquidation, which will lead to wrong state of the market, resulting in direct loss of funds as well as being leveraged from a malicious actors. Main impacts are loss of funds due to fees for the protocol and wrong funding rates, which will cause loses for the LPs and long/short position holders, depending on the sign of funding rate.

Issue happens as there is no function that takes the current skew and OI in the LiquidationBranch::liquidateAccounts.

// update perp market's open interest and skew; we don't enforce ipen
// interest and skew caps during liquidations as:
// 1) open interest and skew are both decreased by liquidations
// 2) we don't want liquidation to be DoS'd in case somehow those cap
// checks would fail
perpMarket.updateOpenInterest(ctx.newOpenInterestX18, ctx.newSkewX18);

Here is a POC prepared, which verifies that after liquidation both of the values are being equal to 0:

forge test --match-test test_skew_and_OI_zeroed
file: LiquidateAccounts_Integration_Test
function test_skew_and_OI_zeroed() public {
uint128 marketId = 2; //ETH/USD market
deal({ token: address(usdz), to: users.naruto.account, give: 100e18 });
MarketConfig memory marketConfig = getFuzzMarketConfig(marketId);
vm.startPrank(users.naruto.account);
createAccountAndDeposit(100e18, address(usdz));
openPosition(marketConfig, 1, 0.01e18, 100e18, true);
vm.stopPrank();
//liquidationKeeper
//liquidate
setAccountsAsLiquidatable(marketConfig, true);
vm.startPrank(liquidationKeeper);
uint128[] memory accs = new uint128[](1);
accs[0] = 1;
perpsEngine.liquidateAccounts(accs);
vm.stopPrank();
int256 skew = (perpsEngine.getSkew(marketId)).intoInt256();
(,, UD60x18 tOI) = perpsEngine.getOpenInterest(marketId);
uint256 totalOpenInterest = tOI.intoUint256();
assertEq(skew, 0);
assertEq(totalOpenInterest, 0);
}

Impact

Direct loss of funds for protocol and traders, as well as wrong state of the Market, due to reseting the skew and OI on each liquidation.

Tools Used

Manual Review

Recommendations

Add the following lines to the LiquidationBranch::liquidateAccounts:

+ (ctx.newOpenInterestX18, ctx.newSkewX18) = perpMarket.checkOpenInterestLimits(-ctx.liquidationSizeX18, ctx.liquidationSizeX18, SD59x18_ZERO);
Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

`liquidateAccounts` calls `updateOpenInterest` with uninitialized OI and skew)

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.