@drift-labs/sdk-browser 2.155.0-beta.3 → 2.155.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/VERSION +1 -1
- package/lib/browser/decode/user.js +8 -5
- package/lib/browser/driftClient.d.ts +15 -10
- package/lib/browser/driftClient.js +137 -23
- package/lib/browser/marginCalculation.d.ts +0 -12
- package/lib/browser/marginCalculation.js +0 -20
- package/lib/browser/math/margin.js +1 -0
- package/lib/browser/math/position.d.ts +1 -0
- package/lib/browser/math/position.js +10 -2
- package/lib/browser/swap/UnifiedSwapClient.js +1 -10
- package/lib/browser/titan/titanClient.d.ts +4 -5
- package/lib/browser/titan/titanClient.js +2 -16
- package/lib/browser/types.d.ts +9 -6
- package/lib/browser/types.js +11 -7
- package/lib/browser/user.js +13 -7
- package/lib/node/decode/user.d.ts.map +1 -1
- package/lib/node/decode/user.js +8 -5
- package/lib/node/driftClient.d.ts +15 -10
- package/lib/node/driftClient.d.ts.map +1 -1
- package/lib/node/driftClient.js +137 -23
- package/lib/node/marginCalculation.d.ts +0 -12
- package/lib/node/marginCalculation.d.ts.map +1 -1
- package/lib/node/marginCalculation.js +0 -20
- package/lib/node/math/margin.d.ts.map +1 -1
- package/lib/node/math/margin.js +1 -0
- package/lib/node/math/position.d.ts +1 -0
- package/lib/node/math/position.d.ts.map +1 -1
- package/lib/node/math/position.js +10 -2
- package/lib/node/math/spotBalance.d.ts.map +1 -1
- package/lib/node/swap/UnifiedSwapClient.d.ts.map +1 -1
- package/lib/node/swap/UnifiedSwapClient.js +1 -10
- package/lib/node/titan/titanClient.d.ts +4 -5
- package/lib/node/titan/titanClient.d.ts.map +1 -1
- package/lib/node/titan/titanClient.js +2 -16
- package/lib/node/types.d.ts +9 -6
- package/lib/node/types.d.ts.map +1 -1
- package/lib/node/types.js +11 -7
- package/lib/node/user.d.ts.map +1 -1
- package/lib/node/user.js +13 -7
- package/package.json +1 -1
- package/scripts/deposit-isolated-positions.ts +110 -0
- package/scripts/find-flagged-users.ts +216 -0
- package/scripts/single-grpc-client-test.ts +71 -21
- package/scripts/withdraw-isolated-positions.ts +174 -0
- package/src/decode/user.ts +14 -6
- package/src/driftClient.ts +297 -65
- package/src/margin/README.md +139 -0
- package/src/marginCalculation.ts +0 -32
- package/src/math/margin.ts +2 -3
- package/src/math/position.ts +12 -2
- package/src/math/spotBalance.ts +0 -1
- package/src/swap/UnifiedSwapClient.ts +2 -13
- package/src/titan/titanClient.ts +4 -28
- package/src/types.ts +11 -7
- package/src/user.ts +17 -8
- package/tests/dlob/helpers.ts +1 -1
- package/tests/user/test.ts +1 -1
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
## Margin Calculation Snapshot (SDK)
|
|
2
|
+
|
|
3
|
+
This document describes the single-source-of-truth margin engine in the SDK that mirrors the on-chain `MarginCalculation` and related semantics. The goal is to compute an immutable snapshot in one pass and have existing `User` getters delegate to it, eliminating duplicative work across getters and UI hooks while maintaining parity with the program.
|
|
4
|
+
|
|
5
|
+
### Alignment with on-chain
|
|
6
|
+
|
|
7
|
+
- The SDK snapshot shape mirrors `programs/drift/src/state/margin_calculation.rs` field-for-field.
|
|
8
|
+
- The inputs and ordering mirror `calculate_margin_requirement_and_total_collateral_and_liability_info` in `programs/drift/src/math/margin.rs`.
|
|
9
|
+
- Isolated positions are represented as `isolatedMarginCalculations` keyed by perp `marketIndex`, matching program logic.
|
|
10
|
+
|
|
11
|
+
### Core SDK types (shape parity)
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
// Types reflect on-chain names and numeric signs
|
|
15
|
+
import { MarketType } from './types';
|
|
16
|
+
|
|
17
|
+
export type MarginCategory = 'Initial' | 'Maintenance' | 'Fill';
|
|
18
|
+
|
|
19
|
+
export type MarginCalculationMode =
|
|
20
|
+
| { type: 'Standard' }
|
|
21
|
+
| { type: 'Liquidation' };
|
|
22
|
+
|
|
23
|
+
export class MarketIdentifier {
|
|
24
|
+
marketType: MarketType;
|
|
25
|
+
marketIndex: number;
|
|
26
|
+
|
|
27
|
+
static spot(marketIndex: number): MarketIdentifier;
|
|
28
|
+
static perp(marketIndex: number): MarketIdentifier;
|
|
29
|
+
equals(other: MarketIdentifier | undefined): boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class MarginContext {
|
|
33
|
+
marginType: MarginCategory;
|
|
34
|
+
mode: MarginCalculationMode;
|
|
35
|
+
strict: boolean;
|
|
36
|
+
ignoreInvalidDepositOracles: boolean;
|
|
37
|
+
isolatedMarginBuffers: Map<number, BN>;
|
|
38
|
+
crossMarginBuffer: BN;
|
|
39
|
+
|
|
40
|
+
// Factory methods
|
|
41
|
+
static standard(marginType: MarginCategory): MarginContext;
|
|
42
|
+
static liquidation(
|
|
43
|
+
crossMarginBuffer: BN,
|
|
44
|
+
isolatedMarginBuffers: Map<number, BN>
|
|
45
|
+
): MarginContext;
|
|
46
|
+
|
|
47
|
+
// Builder methods (return this for chaining)
|
|
48
|
+
strictMode(strict: boolean): this;
|
|
49
|
+
ignoreInvalidDeposits(ignore: boolean): this;
|
|
50
|
+
setCrossMarginBuffer(crossMarginBuffer: BN): this;
|
|
51
|
+
setIsolatedMarginBuffers(isolatedMarginBuffers: Map<number, BN>): this;
|
|
52
|
+
setIsolatedMarginBuffer(marketIndex: number, isolatedMarginBuffer: BN): this;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export class IsolatedMarginCalculation {
|
|
56
|
+
marginRequirement: BN; // u128
|
|
57
|
+
totalCollateral: BN; // i128 (deposit + pnl)
|
|
58
|
+
totalCollateralBuffer: BN; // i128
|
|
59
|
+
marginRequirementPlusBuffer: BN; // u128
|
|
60
|
+
|
|
61
|
+
getTotalCollateralPlusBuffer(): BN;
|
|
62
|
+
meetsMarginRequirement(): boolean;
|
|
63
|
+
meetsMarginRequirementWithBuffer(): boolean;
|
|
64
|
+
marginShortage(): BN;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export class MarginCalculation {
|
|
68
|
+
context: MarginContext;
|
|
69
|
+
|
|
70
|
+
totalCollateral: BN; // i128
|
|
71
|
+
totalCollateralBuffer: BN; // i128
|
|
72
|
+
marginRequirement: BN; // u128
|
|
73
|
+
marginRequirementPlusBuffer: BN; // u128
|
|
74
|
+
|
|
75
|
+
isolatedMarginCalculations: Map<number, IsolatedMarginCalculation>;
|
|
76
|
+
|
|
77
|
+
totalPerpLiabilityValue: BN; // u128
|
|
78
|
+
|
|
79
|
+
// Cross margin helpers
|
|
80
|
+
getCrossTotalCollateralPlusBuffer(): BN;
|
|
81
|
+
meetsCrossMarginRequirement(): boolean;
|
|
82
|
+
meetsCrossMarginRequirementWithBuffer(): boolean;
|
|
83
|
+
getCrossFreeCollateral(): BN;
|
|
84
|
+
|
|
85
|
+
// Combined (cross + isolated) helpers
|
|
86
|
+
meetsMarginRequirement(): boolean;
|
|
87
|
+
meetsMarginRequirementWithBuffer(): boolean;
|
|
88
|
+
|
|
89
|
+
// Isolated margin helpers
|
|
90
|
+
getIsolatedFreeCollateral(marketIndex: number): BN;
|
|
91
|
+
getIsolatedMarginCalculation(marketIndex: number): IsolatedMarginCalculation | undefined;
|
|
92
|
+
hasIsolatedMarginCalculation(marketIndex: number): boolean;
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Computation model (on-demand)
|
|
97
|
+
|
|
98
|
+
- The SDK computes the snapshot on-demand when `getMarginCalculation(...)` is called.
|
|
99
|
+
- No event-driven recomputation by default (oracle prices can change every slot; recomputing every update would be wasteful).
|
|
100
|
+
- Callers (UI/bots) decide polling frequency (e.g., UI can refresh every ~1s on active trade forms).
|
|
101
|
+
|
|
102
|
+
### User integration
|
|
103
|
+
|
|
104
|
+
`User` class provides the primary entrypoint:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
public getMarginCalculation(
|
|
108
|
+
marginCategory: MarginCategory = 'Initial',
|
|
109
|
+
opts?: {
|
|
110
|
+
strict?: boolean; // mirror StrictOraclePrice application
|
|
111
|
+
includeOpenOrders?: boolean; // include open orders in margin calc
|
|
112
|
+
enteringHighLeverage?: boolean; // entering high leverage mode
|
|
113
|
+
liquidationBufferMap?: Map<number | 'cross', BN>; // margin buffer for liquidation mode
|
|
114
|
+
}
|
|
115
|
+
): MarginCalculation;
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Existing getters delegate to the snapshot to avoid duplicate work:
|
|
119
|
+
- `getTotalCollateral()` → `snapshot.totalCollateral`
|
|
120
|
+
- `getMarginRequirement(mode)` → `snapshot.marginRequirement`
|
|
121
|
+
- `getFreeCollateral()` → `snapshot.getCrossFreeCollateral()`
|
|
122
|
+
- Per-market isolated FC → `snapshot.getIsolatedFreeCollateral(marketIndex)`
|
|
123
|
+
|
|
124
|
+
### UI compatibility
|
|
125
|
+
|
|
126
|
+
- All existing `User` getters remain and delegate to the snapshot, so current UI keeps working without call-site changes.
|
|
127
|
+
- New consumers can call `user.getMarginCalculation()` to access isolated breakdowns via `isolatedMarginCalculations`.
|
|
128
|
+
|
|
129
|
+
### Testing and parity
|
|
130
|
+
|
|
131
|
+
- Golden tests comparing SDK snapshot against program outputs (cross and isolated, edge cases).
|
|
132
|
+
- Keep math/rounding identical to program (ordering, buffers, funding, open-order IM, oracle strictness).
|
|
133
|
+
|
|
134
|
+
### Migration plan (brief)
|
|
135
|
+
|
|
136
|
+
1. Implement `types` and `engine` with strict parity; land behind a feature flag.
|
|
137
|
+
2. Add `user.getMarginCalculation()` and delegate legacy getters.
|
|
138
|
+
3. Optionally update UI hooks to read richer fields; not required for compatibility.
|
|
139
|
+
4. Expand parity tests; enable by default after validation.
|
package/src/marginCalculation.ts
CHANGED
|
@@ -131,15 +131,7 @@ export class MarginCalculation {
|
|
|
131
131
|
marginRequirement: BN;
|
|
132
132
|
marginRequirementPlusBuffer: BN;
|
|
133
133
|
isolatedMarginCalculations: Map<number, IsolatedMarginCalculation>;
|
|
134
|
-
allDepositOraclesValid: boolean;
|
|
135
|
-
allLiabilityOraclesValid: boolean;
|
|
136
|
-
withPerpIsolatedLiability: boolean;
|
|
137
|
-
withSpotIsolatedLiability: boolean;
|
|
138
134
|
totalPerpLiabilityValue: BN;
|
|
139
|
-
trackedMarketMarginRequirement: BN;
|
|
140
|
-
fuelDeposits: number;
|
|
141
|
-
fuelBorrows: number;
|
|
142
|
-
fuelPositions: number;
|
|
143
135
|
|
|
144
136
|
constructor(context: MarginContext) {
|
|
145
137
|
this.context = context;
|
|
@@ -148,15 +140,7 @@ export class MarginCalculation {
|
|
|
148
140
|
this.marginRequirement = ZERO;
|
|
149
141
|
this.marginRequirementPlusBuffer = ZERO;
|
|
150
142
|
this.isolatedMarginCalculations = new Map();
|
|
151
|
-
this.allDepositOraclesValid = true;
|
|
152
|
-
this.allLiabilityOraclesValid = true;
|
|
153
|
-
this.withPerpIsolatedLiability = false;
|
|
154
|
-
this.withSpotIsolatedLiability = false;
|
|
155
143
|
this.totalPerpLiabilityValue = ZERO;
|
|
156
|
-
this.trackedMarketMarginRequirement = ZERO;
|
|
157
|
-
this.fuelDeposits = 0;
|
|
158
|
-
this.fuelBorrows = 0;
|
|
159
|
-
this.fuelPositions = 0;
|
|
160
144
|
}
|
|
161
145
|
|
|
162
146
|
addCrossMarginTotalCollateral(delta: BN): void {
|
|
@@ -216,22 +200,6 @@ export class MarginCalculation {
|
|
|
216
200
|
this.totalPerpLiabilityValue.add(perpLiabilityValue);
|
|
217
201
|
}
|
|
218
202
|
|
|
219
|
-
updateAllDepositOraclesValid(valid: boolean): void {
|
|
220
|
-
this.allDepositOraclesValid = this.allDepositOraclesValid && valid;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
updateAllLiabilityOraclesValid(valid: boolean): void {
|
|
224
|
-
this.allLiabilityOraclesValid = this.allLiabilityOraclesValid && valid;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
updateWithSpotIsolatedLiability(isolated: boolean): void {
|
|
228
|
-
this.withSpotIsolatedLiability = this.withSpotIsolatedLiability || isolated;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
updateWithPerpIsolatedLiability(isolated: boolean): void {
|
|
232
|
-
this.withPerpIsolatedLiability = this.withPerpIsolatedLiability || isolated;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
203
|
getCrossTotalCollateralPlusBuffer(): BN {
|
|
236
204
|
return this.totalCollateral.add(this.totalCollateralBuffer);
|
|
237
205
|
}
|
package/src/math/margin.ts
CHANGED
|
@@ -166,10 +166,10 @@ export function calculateWorstCasePerpLiabilityValue(
|
|
|
166
166
|
perpPosition: PerpPosition,
|
|
167
167
|
perpMarket: PerpMarketAccount,
|
|
168
168
|
oraclePrice: BN,
|
|
169
|
-
includeOpenOrders = true
|
|
169
|
+
includeOpenOrders: boolean = true
|
|
170
170
|
): { worstCaseBaseAssetAmount: BN; worstCaseLiabilityValue: BN } {
|
|
171
171
|
const isPredictionMarket = isVariant(perpMarket.contractType, 'prediction');
|
|
172
|
-
|
|
172
|
+
// return early if no open orders required
|
|
173
173
|
if (!includeOpenOrders) {
|
|
174
174
|
return {
|
|
175
175
|
worstCaseBaseAssetAmount: perpPosition.baseAssetAmount,
|
|
@@ -180,7 +180,6 @@ export function calculateWorstCasePerpLiabilityValue(
|
|
|
180
180
|
),
|
|
181
181
|
};
|
|
182
182
|
}
|
|
183
|
-
|
|
184
183
|
const allBids = perpPosition.baseAssetAmount.add(perpPosition.openBids);
|
|
185
184
|
const allAsks = perpPosition.baseAssetAmount.add(perpPosition.openAsks);
|
|
186
185
|
|
package/src/math/position.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
PositionDirection,
|
|
15
15
|
PerpPosition,
|
|
16
16
|
SpotMarketAccount,
|
|
17
|
+
PositionFlag,
|
|
17
18
|
} from '../types';
|
|
18
19
|
import {
|
|
19
20
|
calculateUpdatedAMM,
|
|
@@ -127,7 +128,6 @@ export function calculatePositionPNL(
|
|
|
127
128
|
|
|
128
129
|
if (withFunding) {
|
|
129
130
|
const fundingRatePnL = calculateUnsettledFundingPnl(market, perpPosition);
|
|
130
|
-
|
|
131
131
|
pnl = pnl.add(fundingRatePnL);
|
|
132
132
|
}
|
|
133
133
|
|
|
@@ -244,7 +244,17 @@ export function positionIsAvailable(position: PerpPosition): boolean {
|
|
|
244
244
|
position.baseAssetAmount.eq(ZERO) &&
|
|
245
245
|
position.openOrders === 0 &&
|
|
246
246
|
position.quoteAssetAmount.eq(ZERO) &&
|
|
247
|
-
position.lpShares.eq(ZERO)
|
|
247
|
+
position.lpShares.eq(ZERO) &&
|
|
248
|
+
position.isolatedPositionScaledBalance.eq(ZERO) &&
|
|
249
|
+
!positionIsBeingLiquidated(position)
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function positionIsBeingLiquidated(position: PerpPosition): boolean {
|
|
254
|
+
return (
|
|
255
|
+
(position.positionFlag &
|
|
256
|
+
(PositionFlag.BeingLiquidated | PositionFlag.Bankruptcy)) >
|
|
257
|
+
0
|
|
248
258
|
);
|
|
249
259
|
}
|
|
250
260
|
|
package/src/math/spotBalance.ts
CHANGED
|
@@ -68,7 +68,6 @@ export function getTokenAmount(
|
|
|
68
68
|
balanceType: SpotBalanceType
|
|
69
69
|
): BN {
|
|
70
70
|
const precisionDecrease = TEN.pow(new BN(19 - spotMarket.decimals));
|
|
71
|
-
|
|
72
71
|
if (isVariant(balanceType, 'deposit')) {
|
|
73
72
|
return balanceAmount
|
|
74
73
|
.mul(spotMarket.cumulativeDepositInterest)
|
|
@@ -11,11 +11,7 @@ import {
|
|
|
11
11
|
JupiterClient,
|
|
12
12
|
QuoteResponse as JupiterQuoteResponse,
|
|
13
13
|
} from '../jupiter/jupiterClient';
|
|
14
|
-
import {
|
|
15
|
-
TitanClient,
|
|
16
|
-
QuoteResponse as TitanQuoteResponse,
|
|
17
|
-
SwapMode as TitanSwapMode,
|
|
18
|
-
} from '../titan/titanClient';
|
|
14
|
+
import { TitanClient, SwapMode as TitanSwapMode } from '../titan/titanClient';
|
|
19
15
|
|
|
20
16
|
export type SwapMode = 'ExactIn' | 'ExactOut';
|
|
21
17
|
export type SwapClientType = 'jupiter' | 'titan';
|
|
@@ -167,18 +163,11 @@ export class UnifiedSwapClient {
|
|
|
167
163
|
return { transaction };
|
|
168
164
|
} else {
|
|
169
165
|
const titanClient = this.client as TitanClient;
|
|
170
|
-
const {
|
|
166
|
+
const { userPublicKey } = params;
|
|
171
167
|
|
|
172
168
|
// For Titan, we need to reconstruct the parameters from the quote
|
|
173
|
-
const titanQuote = quote as TitanQuoteResponse;
|
|
174
169
|
const result = await titanClient.getSwap({
|
|
175
|
-
inputMint: new PublicKey(titanQuote.inputMint),
|
|
176
|
-
outputMint: new PublicKey(titanQuote.outputMint),
|
|
177
|
-
amount: new BN(titanQuote.inAmount),
|
|
178
170
|
userPublicKey,
|
|
179
|
-
slippageBps: slippageBps || titanQuote.slippageBps,
|
|
180
|
-
swapMode: titanQuote.swapMode,
|
|
181
|
-
sizeConstraint: 1280 - 375, // MAX_TX_BYTE_SIZE - buffer for drift instructions
|
|
182
171
|
});
|
|
183
172
|
|
|
184
173
|
return {
|
package/src/titan/titanClient.ts
CHANGED
|
@@ -287,21 +287,11 @@ export class TitanClient {
|
|
|
287
287
|
* Get a swap transaction for quote
|
|
288
288
|
*/
|
|
289
289
|
public async getSwap({
|
|
290
|
-
inputMint,
|
|
291
|
-
outputMint,
|
|
292
|
-
amount,
|
|
293
290
|
userPublicKey,
|
|
294
|
-
maxAccounts = 50, // 50 is an estimated amount with buffer
|
|
295
|
-
slippageBps,
|
|
296
|
-
swapMode,
|
|
297
|
-
onlyDirectRoutes,
|
|
298
|
-
excludeDexes,
|
|
299
|
-
sizeConstraint,
|
|
300
|
-
accountsLimitWritable,
|
|
301
291
|
}: {
|
|
302
|
-
inputMint
|
|
303
|
-
outputMint
|
|
304
|
-
amount
|
|
292
|
+
inputMint?: PublicKey;
|
|
293
|
+
outputMint?: PublicKey;
|
|
294
|
+
amount?: BN;
|
|
305
295
|
userPublicKey: PublicKey;
|
|
306
296
|
maxAccounts?: number;
|
|
307
297
|
slippageBps?: number;
|
|
@@ -314,22 +304,8 @@ export class TitanClient {
|
|
|
314
304
|
transactionMessage: TransactionMessage;
|
|
315
305
|
lookupTables: AddressLookupTableAccount[];
|
|
316
306
|
}> {
|
|
317
|
-
const params = this.buildParams({
|
|
318
|
-
inputMint,
|
|
319
|
-
outputMint,
|
|
320
|
-
amount,
|
|
321
|
-
userPublicKey,
|
|
322
|
-
maxAccounts,
|
|
323
|
-
slippageBps,
|
|
324
|
-
swapMode,
|
|
325
|
-
onlyDirectRoutes,
|
|
326
|
-
excludeDexes,
|
|
327
|
-
sizeConstraint,
|
|
328
|
-
accountsLimitWritable,
|
|
329
|
-
});
|
|
330
|
-
|
|
331
307
|
// Check if we have cached quote data that matches the current parameters
|
|
332
|
-
if (!this.lastQuoteData
|
|
308
|
+
if (!this.lastQuoteData) {
|
|
333
309
|
throw new Error(
|
|
334
310
|
'No matching quote data found. Please get a fresh quote before attempting to swap.'
|
|
335
311
|
);
|
package/src/types.ts
CHANGED
|
@@ -591,6 +591,10 @@ export type SpotBankruptcyRecord = {
|
|
|
591
591
|
ifPayment: BN;
|
|
592
592
|
};
|
|
593
593
|
|
|
594
|
+
export class LiquidationBitFlag {
|
|
595
|
+
static readonly IsolatedPosition = 1;
|
|
596
|
+
}
|
|
597
|
+
|
|
594
598
|
export type SettlePnlRecord = {
|
|
595
599
|
ts: BN;
|
|
596
600
|
user: PublicKey;
|
|
@@ -1142,16 +1146,10 @@ export type PerpPosition = {
|
|
|
1142
1146
|
maxMarginRatio: number;
|
|
1143
1147
|
lastQuoteAssetAmountPerLp: BN;
|
|
1144
1148
|
perLpBase: number;
|
|
1145
|
-
isolatedPositionScaledBalance: BN;
|
|
1146
1149
|
positionFlag: number;
|
|
1150
|
+
isolatedPositionScaledBalance: BN;
|
|
1147
1151
|
};
|
|
1148
1152
|
|
|
1149
|
-
export class PositionFlag {
|
|
1150
|
-
static readonly IsolatedPosition = 1;
|
|
1151
|
-
static readonly BeingLiquidated = 2;
|
|
1152
|
-
static readonly Bankruptcy = 4;
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
1153
|
export type UserStatsAccount = {
|
|
1156
1154
|
numberOfSubAccounts: number;
|
|
1157
1155
|
numberOfSubAccountsCreated: number;
|
|
@@ -1302,6 +1300,12 @@ export class OrderParamsBitFlag {
|
|
|
1302
1300
|
static readonly UpdateHighLeverageMode = 2;
|
|
1303
1301
|
}
|
|
1304
1302
|
|
|
1303
|
+
export class PositionFlag {
|
|
1304
|
+
static readonly IsolatedPosition = 1;
|
|
1305
|
+
static readonly BeingLiquidated = 2;
|
|
1306
|
+
static readonly Bankruptcy = 4;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1305
1309
|
export type NecessaryOrderParams = {
|
|
1306
1310
|
orderType: OrderType;
|
|
1307
1311
|
marketIndex: number;
|
package/src/user.ts
CHANGED
|
@@ -504,8 +504,9 @@ export class User {
|
|
|
504
504
|
)
|
|
505
505
|
: ZERO;
|
|
506
506
|
|
|
507
|
-
let freeCollateral: BN;
|
|
508
|
-
if
|
|
507
|
+
let freeCollateral: BN = ZERO;
|
|
508
|
+
// if position is isolated, we always add on available quote from the cross account
|
|
509
|
+
if (positionType === 'isolated') {
|
|
509
510
|
const {
|
|
510
511
|
totalAssetValue: quoteSpotMarketAssetValue,
|
|
511
512
|
totalLiabilityValue: quoteSpotMarketLiabilityValue,
|
|
@@ -520,13 +521,16 @@ export class User {
|
|
|
520
521
|
freeCollateral = quoteSpotMarketAssetValue.sub(
|
|
521
522
|
quoteSpotMarketLiabilityValue
|
|
522
523
|
);
|
|
523
|
-
}
|
|
524
|
-
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// adding free collateral from the cross account or from within isolated margin calc for this marketIndex
|
|
527
|
+
freeCollateral = freeCollateral.add(
|
|
528
|
+
this.getFreeCollateral(
|
|
525
529
|
'Initial',
|
|
526
530
|
enterHighLeverageMode,
|
|
527
531
|
positionType === 'isolated' ? marketIndex : undefined
|
|
528
|
-
).sub(collateralBuffer)
|
|
529
|
-
|
|
532
|
+
).sub(collateralBuffer)
|
|
533
|
+
);
|
|
530
534
|
|
|
531
535
|
return this.getPerpBuyingPowerFromFreeCollateralAndBaseAssetAmount(
|
|
532
536
|
marketIndex,
|
|
@@ -574,7 +578,12 @@ export class User {
|
|
|
574
578
|
});
|
|
575
579
|
|
|
576
580
|
if (perpMarketIndex !== undefined) {
|
|
577
|
-
|
|
581
|
+
// getIsolatedFreeCollateral will throw if no existing isolated position but we are fetching for potential new position, so we wrap in a try/catch
|
|
582
|
+
try {
|
|
583
|
+
return calc.getIsolatedFreeCollateral(perpMarketIndex);
|
|
584
|
+
} catch (error) {
|
|
585
|
+
return ZERO;
|
|
586
|
+
}
|
|
578
587
|
} else {
|
|
579
588
|
return calc.getCrossFreeCollateral();
|
|
580
589
|
}
|
|
@@ -2363,7 +2372,7 @@ export class User {
|
|
|
2363
2372
|
enteringHighLeverage
|
|
2364
2373
|
);
|
|
2365
2374
|
|
|
2366
|
-
if (freeCollateralDelta.eq(ZERO)) {
|
|
2375
|
+
if (!freeCollateralDelta || freeCollateralDelta.eq(ZERO)) {
|
|
2367
2376
|
return new BN(-1);
|
|
2368
2377
|
}
|
|
2369
2378
|
|
package/tests/dlob/helpers.ts
CHANGED
package/tests/user/test.ts
CHANGED
|
@@ -49,7 +49,7 @@ async function makeMockUser(
|
|
|
49
49
|
oraclePriceMap[myMockSpotMarkets[i].oracle.toString()] =
|
|
50
50
|
spotOraclePriceList[i];
|
|
51
51
|
}
|
|
52
|
-
// console.log(oraclePriceMap);
|
|
52
|
+
// console.log('oraclePriceMap:', oraclePriceMap);
|
|
53
53
|
|
|
54
54
|
function getMockUserAccount(): UserAccount {
|
|
55
55
|
return myMockUserAccount;
|