Consider a scenario where a user opens a leveraged positions in an active market. Their account becomes liquidatable due to insufficient margin. The market is then paused or disabled by calling marketMakingEngine::pauseMarket
function.
The transaction reverts instead of allowing liquidation.
/2025-01-zaros-part-2/test/integration/perpetuals/liquidation-branch/liquidateAccounts/liquidateAccounts.t.sol:100
100: function test_revert_liquidation_call()
101: external
102: givenTheSenderIsARegisteredLiquidator
103: whenTheAccountsIdsArrayIsNotEmpty
104: givenAllAccountsExist
105: {
106:
107: uint256 marketId = 3.117e76;
108: uint256 secondMarketId=14618;
109: bool isLong=false;
110: uint256 amountOfTradingAccounts =2992;
111: uint256 timeDelta = 19068;
112: TestFuzz_GivenThereAreLiquidatableAccountsInTheArray_Context memory ctx;
113:
114: ctx.fuzzMarketConfig = getFuzzMarketConfig(marketId);
115: ctx.secondMarketConfig = getFuzzMarketConfig(secondMarketId);
116:
117: vm.assume(ctx.fuzzMarketConfig.marketId != ctx.secondMarketConfig.marketId);
118:
119: amountOfTradingAccounts = bound({ x: amountOfTradingAccounts, min: 1, max: 10 });
120: timeDelta = bound({ x: timeDelta, min: 1 seconds, max: 1 days });
121:
122: ctx.marginValueUsd = 10_000e18 / amountOfTradingAccounts;
123: ctx.initialMarginRate = ctx.fuzzMarketConfig.imr;
124:
125: deal({ token: address(usdToken), to: users.naruto.account, give: ctx.marginValueUsd });
126:
127:
128: ctx.accountsIds = new uint128[](amountOfTradingAccounts + 2);
129: ctx.accountsUnrealizedPnl = new SD59x18[](amountOfTradingAccounts + 2);
130: ctx.accountsMarginBalanceInitial = new SD59x18[](amountOfTradingAccounts + 2);
131:
132: ctx.accountMarginValueUsd = ctx.marginValueUsd / (amountOfTradingAccounts + 1);
133:
134: for (uint256 i; i < amountOfTradingAccounts; i++) {
135: ctx.tradingAccountId = createAccountAndDeposit(ctx.accountMarginValueUsd, address(usdToken));
136:
137: openPosition(
138: ctx.fuzzMarketConfig,
139: ctx.tradingAccountId,
140: ctx.initialMarginRate,
141: ctx.accountMarginValueUsd / 2,
142: isLong
143: );
144:
145: openPosition(
146: ctx.secondMarketConfig,
147: ctx.tradingAccountId,
148: ctx.secondMarketConfig.imr,
149: ctx.accountMarginValueUsd / 2,
150: isLong
151: );
152:
153: ctx.accountsIds[i] = ctx.tradingAccountId;
154: ctx.accountsMarginBalanceInitial[i] =
155: perpsEngine.exposed_getMarginBalanceUsd(ctx.accountsIds[i], sd59x18(0));
156:
157: deal({ token: address(usdToken), to: users.naruto.account, give: ctx.marginValueUsd });
158: }
159:
160: setAccountsAsLiquidatable(ctx.fuzzMarketConfig, isLong);
161: setAccountsAsLiquidatable(ctx.secondMarketConfig, isLong);
162:
163: ctx.nonLiquidatableTradingAccountId = createAccountAndDeposit(ctx.accountMarginValueUsd, address(usdToken));
164: openPosition(
165: ctx.fuzzMarketConfig,
166: ctx.nonLiquidatableTradingAccountId,
167: ctx.fuzzMarketConfig.imr,
168: ctx.accountMarginValueUsd / 2,
169: isLong
170: );
171:
172: changePrank({ msgSender: liquidationKeeper });
173:
174: skip(timeDelta);
175:
176: for (uint256 i; i < ctx.accountsIds.length; i++) {
177: (,, ctx.accountsUnrealizedPnl[i]) = perpsEngine.exposed_getAccountMarginRequirementUsdAndUnrealizedPnlUsd(
178: ctx.accountsIds[i], 0, sd59x18(0)
179: );
180:
181: if (ctx.accountsIds[i] == ctx.nonLiquidatableTradingAccountId || ctx.accountsIds[i] == 0) {
182: continue;
183: }
184: }
185:
186: ctx.expectedLastFundingRate = perpsEngine.getFundingRate(ctx.fuzzMarketConfig.marketId).intoInt256();
187: ctx.expectedLastFundingTime = block.timestamp;
188:
189: changePrank({ msgSender: users.owner.account });
190: marketMakingEngine.pauseMarket(ctx.fuzzMarketConfig.marketId);
191:
192: changePrank({ msgSender: liquidationKeeper });
193: perpsEngine.liquidateAccounts(ctx.accountsIds);
194: }
Unliquidated accounts continue holding bad debt, increasing risk for the protocol. In case market is not Live on MarketMakingEngine
.
Consider allowing liquidation even if the market is paused, ensuring risk is managed.