Skip to content

Commit 4adb6fc

Browse files
committed
Update #removeModule logic to handle case where account balance is negative
1 parent 892ba14 commit 4adb6fc

File tree

3 files changed

+67
-5
lines changed

3 files changed

+67
-5
lines changed

contracts/protocol/modules/PerpV2LeverageModule.sol

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,9 +381,15 @@ contract PerpV2LeverageModule is ModuleBase, ReentrancyGuard, Ownable, AllowSetT
381381
// errors when calculating the notional quantity as `collateralUnits.preciseMul(totalSupply)`,
382382
// there's a good chance we will be left with a single USDC unit in the Perp vault which we
383383
// should ignore.
384+
//
385+
// Additionally, we need to allow for the possibility that the collateral balance will be positive
386+
// while the account value is negative. This "invalid state" can occur if the account is liquidated
387+
// and can't be settled to zero because prices have moved against the position.
388+
//
389+
// TODO: We need info from Perp about how to clear this state
384390
require(
385-
_getCollateralBalance(setToken).fromPreciseUnitToDecimals(collateralDecimals) <= 1,
386-
"Collateral balance remaining"
391+
perpClearingHouse.getAccountValue(address(setToken)).fromPreciseUnitToDecimals(collateralDecimals) <= 1,
392+
"Account balance is positive"
387393
);
388394

389395
delete positions[setToken]; // Should already be empty

test/integration/perpV2LeverageSlippageIssuance.spec.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
} from "@utils/test/index";
3333
import { PerpV2Fixture, SystemFixture } from "@utils/fixtures";
3434
import { BigNumber } from "ethers";
35-
import { ADDRESS_ZERO, ZERO } from "@utils/constants";
35+
import { ADDRESS_ZERO, ZERO, MAX_UINT_256, ZERO_BYTES } from "@utils/constants";
3636

3737
const expect = getWaffleExpect();
3838

@@ -1120,6 +1120,62 @@ describe("PerpV2LeverageSlippageIssuance", () => {
11201120
expect(toUSDCDecimals(finalCollateralBalance)).to.be.closeTo(expectedCollateralBalance, 2);
11211121
});
11221122
});
1123+
1124+
describe("when liquidation results in negative account value", () => {
1125+
beforeEach(async () => {
1126+
// Calculated leverage = ~8.5X = 8_654_438_822_995_683_587
1127+
await leverUp(
1128+
setToken,
1129+
perpLeverageModule,
1130+
perpSetup,
1131+
owner,
1132+
baseToken,
1133+
6,
1134+
ether(.02),
1135+
true
1136+
);
1137+
1138+
// Move oracle price down to 5 USDC to enable liquidation
1139+
await perpSetup.setBaseTokenOraclePrice(vETH, usdcUnits(5.0));
1140+
1141+
// Move price down by maker selling 20k USDC of vETH
1142+
// Post trade spot price rises from ~10 USDC to 6_370_910_537_702_299_856
1143+
await perpSetup.clearingHouse.connect(maker.wallet).openPosition({
1144+
baseToken: vETH.address,
1145+
isBaseToQuote: true, // short
1146+
isExactInput: false, // `amount` is USDC
1147+
amount: ether(20000),
1148+
oppositeAmountBound: ZERO,
1149+
deadline: MAX_UINT_256,
1150+
sqrtPriceLimitX96: ZERO,
1151+
referralCode: ZERO_BYTES
1152+
});
1153+
1154+
await perpSetup
1155+
.clearingHouse
1156+
.connect(otherTrader.wallet)
1157+
.liquidate(subjectSetToken, baseToken);
1158+
});
1159+
1160+
it("should be possible to remove the module", async () => {
1161+
await subject();
1162+
1163+
const collateralBalance = await perpSetup.vault.getBalance(subjectSetToken);
1164+
const freeCollateral = await perpSetup.vault.getFreeCollateral(subjectSetToken);
1165+
const accountValue = await perpSetup.clearingHouse.getAccountValue(subjectSetToken);
1166+
1167+
// collateralBalance: 20_100_000 (10^6)
1168+
// accountValue: -43_466_857_276_051_287_954 (10^18)
1169+
expect(collateralBalance).gt(1);
1170+
expect(freeCollateral).eq(0);
1171+
expect(accountValue).lt(-1);
1172+
1173+
/// Remove module
1174+
await setToken.removeModule(perpLeverageModule.address);
1175+
const finalModules = await setToken.getModules();
1176+
expect(finalModules.includes(perpLeverageModule.address)).eq(false);
1177+
});
1178+
});
11231179
});
11241180
});
11251181
});

test/protocol/modules/perpV2LeverageModule.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4691,13 +4691,13 @@ describe("PerpV2LeverageModule", () => {
46914691
expect(isRegistered).to.be.false;
46924692
});
46934693

4694-
describe("when collateral balance exists", async () => {
4694+
describe("when the account balance is positive", async () => {
46954695
beforeEach(async () => {
46964696
await perpLeverageModule.deposit(setToken.address, usdcUnits(10));
46974697
});
46984698

46994699
it("should revert", async () => {
4700-
await expect(subject()).to.be.revertedWith("Collateral balance remaining");
4700+
await expect(subject()).to.be.revertedWith("Account balance is positive");
47014701
});
47024702
});
47034703
});

0 commit comments

Comments
 (0)