Skip to content

Commit 705d13a

Browse files
committed
fix: some pr feedback, additional undefined checks + better margin logic
1 parent e9872e1 commit 705d13a

File tree

1 file changed

+63
-27
lines changed

1 file changed

+63
-27
lines changed

sdk/src/user.ts

Lines changed: 63 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,11 @@ import { StrictOraclePrice } from './oracles/strictOraclePrice';
108108

109109
import { calculateSpotFuelBonus, calculatePerpFuelBonus } from './math/fuel';
110110
import { grpcUserAccountSubscriber } from './accounts/grpcUserAccountSubscriber';
111-
import { MarginCalculation, MarginContext } from './marginCalculation';
111+
import {
112+
IsolatedMarginCalculation,
113+
MarginCalculation,
114+
MarginContext,
115+
} from './marginCalculation';
112116

113117
export type MarginType = 'Cross' | 'Isolated';
114118

@@ -350,6 +354,7 @@ export class User {
350354

351355
public getIsolatePerpPositionTokenAmount(perpMarketIndex: number): BN {
352356
const perpPosition = this.getPerpPosition(perpMarketIndex);
357+
if (!perpPosition) return ZERO;
353358
const perpMarket = this.driftClient.getPerpMarketAccount(perpMarketIndex);
354359
const spotMarket = this.driftClient.getSpotMarketAccount(
355360
perpMarket.quoteSpotMarketIndex
@@ -609,6 +614,7 @@ export class User {
609614
if (perpMarketIndex !== undefined) {
610615
const isolatedMarginCalculation =
611616
marginCalc.isolatedMarginCalculations.get(perpMarketIndex);
617+
if (!isolatedMarginCalculation) return ZERO;
612618
const { marginRequirement, marginRequirementPlusBuffer } =
613619
isolatedMarginCalculation;
614620

@@ -1281,11 +1287,15 @@ export class User {
12811287
* @returns : number (value from [0, 100])
12821288
*/
12831289
public getHealth(perpMarketIndex?: number): number {
1284-
const marginCalc = this.getMarginCalculation('Maintenance');
1285-
if (this.isCrossMarginBeingLiquidated(marginCalc) && !perpMarketIndex) {
1290+
if (this.isCrossMarginBeingLiquidated() && !perpMarketIndex) {
1291+
return 0;
1292+
}
1293+
if (this.hasIsolatedPositionBeingLiquidated() && perpMarketIndex) {
12861294
return 0;
12871295
}
12881296

1297+
const marginCalc = this.getMarginCalculation('Maintenance');
1298+
12891299
let totalCollateral: BN;
12901300
let maintenanceMarginReq: BN;
12911301

@@ -1671,8 +1681,14 @@ export class User {
16711681
oraclePriceData
16721682
);
16731683

1684+
const tokenAmount = getTokenAmount(
1685+
perpPosition.isolatedPositionScaledBalance ?? ZERO,
1686+
quoteSpotMarket,
1687+
SpotBalanceType.DEPOSIT
1688+
);
1689+
16741690
const spotAssetValue = getStrictTokenValue(
1675-
perpPosition.isolatedPositionScaledBalance ?? ZERO, //TODO remove ? later
1691+
tokenAmount,
16761692
quoteSpotMarket.decimals,
16771693
strictOracle
16781694
);
@@ -2051,23 +2067,49 @@ export class User {
20512067
);
20522068
}
20532069

2070+
public isCrossMarginBeingLiquidated(): boolean {
2071+
return (
2072+
(this.getUserAccount().status &
2073+
(UserStatus.BEING_LIQUIDATED | UserStatus.BANKRUPT)) >
2074+
0
2075+
);
2076+
}
2077+
20542078
/** Returns true if cross margin is currently below maintenance requirement (no buffer). */
2055-
public isCrossMarginBeingLiquidated(marginCalc?: MarginCalculation): boolean {
2079+
public canCrossMarginBeLiquidated(marginCalc?: MarginCalculation): boolean {
20562080
const calc = marginCalc ?? this.getMarginCalculation('Maintenance');
20572081
return calc.totalCollateral.lt(calc.marginRequirement);
20582082
}
20592083

2084+
public hasIsolatedPositionBeingLiquidated(): boolean {
2085+
return this.getActivePerpPositions().some(
2086+
(position) =>
2087+
(position.positionFlag &
2088+
(PositionFlag.BeingLiquidated | PositionFlag.Bankruptcy)) >
2089+
0
2090+
);
2091+
}
2092+
20602093
/** Returns true if any isolated perp position is currently below its maintenance requirement (no buffer). */
2061-
public isIsolatedMarginBeingLiquidated(
2094+
public getLiquidatableIsolatedPositions(
20622095
marginCalc?: MarginCalculation
2063-
): boolean {
2096+
): number[] {
2097+
const liquidatableIsolatedPositions = [];
20642098
const calc = marginCalc ?? this.getMarginCalculation('Maintenance');
2065-
for (const [, isoCalc] of calc.isolatedMarginCalculations) {
2066-
if (isoCalc.totalCollateral.lt(isoCalc.marginRequirement)) {
2067-
return true;
2099+
for (const [marketIndex, isoCalc] of calc.isolatedMarginCalculations) {
2100+
if (this.canIsolatedPositionMarginBeLiquidated(isoCalc)) {
2101+
liquidatableIsolatedPositions.push(marketIndex);
20682102
}
20692103
}
2070-
return false;
2104+
return liquidatableIsolatedPositions;
2105+
}
2106+
2107+
public canIsolatedPositionMarginBeLiquidated(
2108+
isolatedMarginCalculation: IsolatedMarginCalculation
2109+
): boolean {
2110+
return isolatedMarginCalculation.totalCollateral.lt(
2111+
isolatedMarginCalculation.marginRequirement
2112+
);
20712113
}
20722114

20732115
public hasStatus(status: UserStatus): boolean {
@@ -2247,6 +2289,7 @@ export class User {
22472289
});
22482290
const isolatedMarginCalculation =
22492291
marginCalculation.isolatedMarginCalculations.get(marketIndex);
2292+
if (!isolatedMarginCalculation) return ZERO;
22502293
const { totalCollateral, marginRequirement } = isolatedMarginCalculation;
22512294

22522295
const freeCollateral = BN.max(
@@ -2287,10 +2330,6 @@ export class User {
22872330
includeOpenOrders
22882331
);
22892332

2290-
// console.log(
2291-
// 'new user liq price totalCollateral',
2292-
// totalCollateral.toString()
2293-
// );
22942333
const marginRequirement = this.getMarginRequirement(
22952334
marginCategory,
22962335
undefined,
@@ -2299,10 +2338,6 @@ export class User {
22992338
enteringHighLeverage
23002339
);
23012340

2302-
// console.log(
2303-
// 'new user liq price marginRequirement',
2304-
// marginRequirement.toString()
2305-
// );
23062341
let freeCollateral = BN.max(
23072342
ZERO,
23082343
totalCollateral.sub(marginRequirement)
@@ -2514,7 +2549,6 @@ export class User {
25142549
this.getUserAccount().maxMarginRatio
25152550
);
25162551

2517-
// TODO: does this work in an isolated position context, cc perp
25182552
const marginRatio = calculateMarketMarginRatio(
25192553
market,
25202554
proposedBaseAssetAmount.abs(),
@@ -4467,6 +4501,15 @@ export class User {
44674501
}
44684502
}
44694503

4504+
// perp position liability
4505+
const hasPerpLiability =
4506+
!marketPosition.baseAssetAmount.eq(ZERO) ||
4507+
marketPosition.quoteAssetAmount.lt(ZERO) ||
4508+
marketPosition.openOrders !== 0;
4509+
if (hasPerpLiability) {
4510+
calc.addPerpLiability();
4511+
}
4512+
44704513
// Add perp contribution: isolated vs cross
44714514
const isIsolated = this.isPerpPositionIsolated(marketPosition);
44724515
if (isIsolated) {
@@ -4513,13 +4556,6 @@ export class User {
45134556
worstCaseLiabilityValue
45144557
);
45154558
calc.addCrossMarginTotalCollateral(positionUnrealizedPnl);
4516-
const hasPerpLiability =
4517-
!marketPosition.baseAssetAmount.eq(ZERO) ||
4518-
marketPosition.quoteAssetAmount.lt(ZERO) ||
4519-
marketPosition.openOrders !== 0;
4520-
if (hasPerpLiability) {
4521-
calc.addPerpLiability();
4522-
}
45234559
}
45244560
}
45254561
return calc;

0 commit comments

Comments
 (0)