Skip to content

Commit 46cc352

Browse files
committed
feat: revamp liquidation checker functions for cross vs iso margin
1 parent 826c962 commit 46cc352

File tree

1 file changed

+74
-28
lines changed

1 file changed

+74
-28
lines changed

sdk/src/user.ts

Lines changed: 74 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,10 @@ import { StrictOraclePrice } from './oracles/strictOraclePrice';
108108
import { calculateSpotFuelBonus, calculatePerpFuelBonus } from './math/fuel';
109109
import { grpcUserAccountSubscriber } from './accounts/grpcUserAccountSubscriber';
110110
import {
111-
MarginCalculation as JsMarginCalculation,
111+
MarginCalculation,
112112
MarginContext,
113113
} from './marginCalculation';
114114

115-
// Backwards compatibility: alias SDK MarginCalculation shape
116-
export type UserMarginCalculation = JsMarginCalculation;
117-
118115
export type MarginType = 'Cross' | 'Isolated';
119116

120117
export class User {
@@ -143,7 +140,7 @@ export class User {
143140
liquidationBuffer?: BN; // margin_buffer analog for buffer mode
144141
marginRatioOverride?: number; // mirrors context.margin_ratio_override
145142
}
146-
): JsMarginCalculation {
143+
): MarginCalculation {
147144
const strict = opts?.strict ?? false;
148145
const enteringHighLeverage = opts?.enteringHighLeverage ?? false;
149146
const includeOpenOrders = opts?.includeOpenOrders ?? true; // TODO: remove this ??
@@ -165,7 +162,7 @@ export class User {
165162
.strictMode(strict)
166163
.setMarginBuffer(marginBuffer)
167164
.setMarginRatioOverride(userCustomMarginRatio);
168-
const calc = new JsMarginCalculation(ctx);
165+
const calc = new MarginCalculation(ctx);
169166

170167
// SPOT POSITIONS
171168
// TODO: include open orders in the worst-case simulation in the same way on both spot and perp positions
@@ -2222,41 +2219,90 @@ export class User {
22222219
return netAssetValue.mul(TEN_THOUSAND).div(totalLiabilityValue);
22232220
}
22242221

2225-
public canBeLiquidated(perpMarketIndex?: number): {
2222+
public canBeLiquidated(): {
22262223
canBeLiquidated: boolean;
22272224
marginRequirement: BN;
22282225
totalCollateral: BN;
22292226
} {
2230-
const liquidationBuffer = this.getLiquidationBuffer();
2227+
// Deprecated signature retained for backward compatibility in type only
2228+
// but implementation now delegates to the new Map-based API and returns cross margin status.
2229+
const map = this.getLiquidationStatuses();
2230+
const cross = map.get('cross');
2231+
return cross ?? { canBeLiquidated: false, marginRequirement: ZERO, totalCollateral: ZERO };
2232+
}
22312233

2232-
const totalCollateral = this.getTotalCollateral(
2233-
'Maintenance',
2234-
undefined,
2235-
undefined,
2236-
liquidationBuffer
2237-
);
2234+
/**
2235+
* New API: Returns liquidation status for cross and each isolated perp position.
2236+
* Map keys:
2237+
* - 'cross' for cross margin
2238+
* - marketIndex (number) for each isolated perp position
2239+
*/
2240+
public getLiquidationStatuses(marginCalc?: MarginCalculation): Map<'cross' | number, { canBeLiquidated: boolean; marginRequirement: BN; totalCollateral: BN }> {
2241+
// If not provided, use buffer-aware calc for canBeLiquidated checks
2242+
if (!marginCalc) {
2243+
const liquidationBuffer = this.getLiquidationBuffer();
2244+
marginCalc = this.getMarginCalculation('Maintenance', { liquidationBuffer });
2245+
}
22382246

2239-
const marginRequirement = this.getMaintenanceMarginRequirement(
2240-
liquidationBuffer,
2241-
perpMarketIndex
2242-
);
2243-
const canBeLiquidated = totalCollateral.lt(marginRequirement);
2247+
const result = new Map<'cross' | number, {
2248+
canBeLiquidated: boolean;
2249+
marginRequirement: BN;
2250+
totalCollateral: BN;
2251+
}>();
2252+
2253+
// Cross margin status
2254+
const crossTotalCollateral = marginCalc.totalCollateral;
2255+
const crossMarginRequirement = marginCalc.marginRequirement;
2256+
result.set('cross', {
2257+
canBeLiquidated: crossTotalCollateral.lt(crossMarginRequirement),
2258+
marginRequirement: crossMarginRequirement,
2259+
totalCollateral: crossTotalCollateral,
2260+
});
22442261

2245-
return {
2246-
canBeLiquidated,
2247-
marginRequirement,
2248-
totalCollateral,
2249-
};
2262+
// Isolated positions status
2263+
for (const [marketIndex, isoCalc] of marginCalc.isolatedMarginCalculations) {
2264+
const isoTotalCollateral = isoCalc.totalCollateral;
2265+
const isoMarginRequirement = isoCalc.marginRequirement;
2266+
result.set(marketIndex, {
2267+
canBeLiquidated: isoTotalCollateral.lt(isoMarginRequirement),
2268+
marginRequirement: isoMarginRequirement,
2269+
totalCollateral: isoTotalCollateral,
2270+
});
2271+
}
2272+
2273+
return result;
22502274
}
22512275

2252-
public isBeingLiquidated(): boolean {
2253-
return (
2276+
public isBeingLiquidated(marginCalc?: MarginCalculation): boolean {
2277+
// Consider on-chain flags OR computed margin status (cross or any isolated)
2278+
const hasOnChainFlag =
22542279
(this.getUserAccount().status &
2255-
(UserStatus.BEING_LIQUIDATED | UserStatus.BANKRUPT)) >
2256-
0
2280+
(UserStatus.BEING_LIQUIDATED | UserStatus.BANKRUPT)) > 0;
2281+
const calc = marginCalc ?? this.getMarginCalculation('Maintenance');
2282+
return (
2283+
hasOnChainFlag ||
2284+
this.isCrossMarginBeingLiquidated(calc) ||
2285+
this.isIsolatedMarginBeingLiquidated(calc)
22572286
);
22582287
}
22592288

2289+
/** Returns true if cross margin is currently below maintenance requirement (no buffer). */
2290+
public isCrossMarginBeingLiquidated(marginCalc?: MarginCalculation): boolean {
2291+
const calc = marginCalc ?? this.getMarginCalculation('Maintenance');
2292+
return calc.totalCollateral.lt(calc.marginRequirement);
2293+
}
2294+
2295+
/** Returns true if any isolated perp position is currently below its maintenance requirement (no buffer). */
2296+
public isIsolatedMarginBeingLiquidated(marginCalc?: MarginCalculation): boolean {
2297+
const calc = marginCalc ?? this.getMarginCalculation('Maintenance');
2298+
for (const [, isoCalc] of calc.isolatedMarginCalculations) {
2299+
if (isoCalc.totalCollateral.lt(isoCalc.marginRequirement)) {
2300+
return true;
2301+
}
2302+
}
2303+
return false;
2304+
}
2305+
22602306
public hasStatus(status: UserStatus): boolean {
22612307
return (this.getUserAccount().status & status) > 0;
22622308
}

0 commit comments

Comments
 (0)