@drift-labs/sdk 2.37.1-beta.1 → 2.37.1-beta.10
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/adminClient.d.ts +1 -0
- package/lib/adminClient.js +10 -0
- package/lib/constants/perpMarkets.js +20 -0
- package/lib/idl/drift.json +73 -8
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/math/auction.js +1 -1
- package/lib/orderSubscriber/OrderSubscriber.d.ts +5 -1
- package/lib/orderSubscriber/OrderSubscriber.js +10 -0
- package/lib/orderSubscriber/types.d.ts +5 -0
- package/lib/tx/baseTxSender.d.ts +30 -0
- package/lib/tx/baseTxSender.js +176 -0
- package/lib/tx/fastSingleTxSender.d.ts +27 -0
- package/lib/tx/fastSingleTxSender.js +83 -0
- package/lib/tx/retryTxSender.d.ts +5 -14
- package/lib/tx/retryTxSender.js +7 -158
- package/lib/types.d.ts +18 -0
- package/lib/types.js +1 -0
- package/lib/user.d.ts +10 -1
- package/lib/user.js +259 -61
- package/package.json +2 -2
- package/src/adminClient.ts +18 -0
- package/src/constants/perpMarkets.ts +20 -0
- package/src/idl/drift.json +73 -8
- package/src/index.ts +1 -0
- package/src/marinade/types.ts +70 -70
- package/src/math/auction.ts +1 -1
- package/src/orderSubscriber/OrderSubscriber.ts +19 -2
- package/src/orderSubscriber/types.ts +11 -0
- package/src/tx/baseTxSender.ts +276 -0
- package/src/tx/fastSingleTxSender.ts +142 -0
- package/src/tx/retryTxSender.ts +9 -235
- package/src/types.ts +19 -0
- package/src/user.ts +441 -101
- package/tests/amm/test.ts +83 -39
- package/tests/dlob/helpers.ts +2 -0
- package/tests/dlob/test.ts +19 -17
package/src/types.ts
CHANGED
|
@@ -31,6 +31,7 @@ export class UserStatus {
|
|
|
31
31
|
static readonly ACTIVE = { active: {} };
|
|
32
32
|
static readonly BEING_LIQUIDATED = { beingLiquidated: {} };
|
|
33
33
|
static readonly BANKRUPT = { bankrupt: {} };
|
|
34
|
+
static readonly REDUCE_ONLY = { reduceOnly: {} };
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
export class ContractType {
|
|
@@ -766,6 +767,8 @@ export type AMM = {
|
|
|
766
767
|
bidQuoteAssetReserve: BN;
|
|
767
768
|
askBaseAssetReserve: BN;
|
|
768
769
|
askQuoteAssetReserve: BN;
|
|
770
|
+
|
|
771
|
+
perLpBase: number; // i8
|
|
769
772
|
};
|
|
770
773
|
|
|
771
774
|
// # User Account Types
|
|
@@ -784,6 +787,7 @@ export type PerpPosition = {
|
|
|
784
787
|
remainderBaseAssetAmount: number;
|
|
785
788
|
lastBaseAssetAmountPerLp: BN;
|
|
786
789
|
lastQuoteAssetAmountPerLp: BN;
|
|
790
|
+
perLpBase: number;
|
|
787
791
|
};
|
|
788
792
|
|
|
789
793
|
export type UserStatsAccount = {
|
|
@@ -1099,3 +1103,18 @@ export type PerpMarketExtendedInfo = {
|
|
|
1099
1103
|
pnlPoolValue: BN;
|
|
1100
1104
|
contractTier: ContractTier;
|
|
1101
1105
|
};
|
|
1106
|
+
|
|
1107
|
+
export type HealthComponents = {
|
|
1108
|
+
deposits: HealthComponent[];
|
|
1109
|
+
borrows: HealthComponent[];
|
|
1110
|
+
perpPositions: HealthComponent[];
|
|
1111
|
+
perpPnl: HealthComponent[];
|
|
1112
|
+
};
|
|
1113
|
+
|
|
1114
|
+
export type HealthComponent = {
|
|
1115
|
+
marketIndex: number;
|
|
1116
|
+
size: BN;
|
|
1117
|
+
value: BN;
|
|
1118
|
+
weight: BN;
|
|
1119
|
+
weightedValue: BN;
|
|
1120
|
+
};
|
package/src/user.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
SpotPosition,
|
|
12
12
|
isOneOfVariant,
|
|
13
13
|
PerpMarketAccount,
|
|
14
|
+
HealthComponents,
|
|
14
15
|
} from './types';
|
|
15
16
|
import { calculateEntryPrice, positionIsAvailable } from './math/position';
|
|
16
17
|
import {
|
|
@@ -30,6 +31,7 @@ import {
|
|
|
30
31
|
BASE_PRECISION,
|
|
31
32
|
ONE,
|
|
32
33
|
TWO,
|
|
34
|
+
AMM_RESERVE_PRECISION_EXP,
|
|
33
35
|
} from './constants/numericConstants';
|
|
34
36
|
import {
|
|
35
37
|
UserAccountSubscriber,
|
|
@@ -263,6 +265,7 @@ export class User {
|
|
|
263
265
|
lpShares: ZERO,
|
|
264
266
|
lastBaseAssetAmountPerLp: ZERO,
|
|
265
267
|
lastQuoteAssetAmountPerLp: ZERO,
|
|
268
|
+
perLpBase: 0,
|
|
266
269
|
};
|
|
267
270
|
}
|
|
268
271
|
|
|
@@ -426,21 +429,62 @@ export class User {
|
|
|
426
429
|
}
|
|
427
430
|
|
|
428
431
|
const position = this.getClonedPosition(originalPosition);
|
|
429
|
-
|
|
430
432
|
const market = this.driftClient.getPerpMarketAccount(position.marketIndex);
|
|
433
|
+
|
|
434
|
+
if (market.amm.perLpBase != position.perLpBase) {
|
|
435
|
+
// perLpBase = 1 => per 10 LP shares, perLpBase = -1 => per 0.1 LP shares
|
|
436
|
+
const expoDiff = market.amm.perLpBase - position.perLpBase;
|
|
437
|
+
const marketPerLpRebaseScalar = new BN(10 ** Math.abs(expoDiff));
|
|
438
|
+
|
|
439
|
+
if (expoDiff > 0) {
|
|
440
|
+
position.lastBaseAssetAmountPerLp =
|
|
441
|
+
position.lastBaseAssetAmountPerLp.mul(marketPerLpRebaseScalar);
|
|
442
|
+
position.lastQuoteAssetAmountPerLp =
|
|
443
|
+
position.lastQuoteAssetAmountPerLp.mul(marketPerLpRebaseScalar);
|
|
444
|
+
} else {
|
|
445
|
+
position.lastBaseAssetAmountPerLp =
|
|
446
|
+
position.lastBaseAssetAmountPerLp.div(marketPerLpRebaseScalar);
|
|
447
|
+
position.lastQuoteAssetAmountPerLp =
|
|
448
|
+
position.lastQuoteAssetAmountPerLp.div(marketPerLpRebaseScalar);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
position.perLpBase = position.perLpBase + expoDiff;
|
|
452
|
+
}
|
|
453
|
+
|
|
431
454
|
const nShares = position.lpShares;
|
|
432
455
|
|
|
433
456
|
// incorp unsettled funding on pre settled position
|
|
434
457
|
const quoteFundingPnl = calculatePositionFundingPNL(market, position);
|
|
435
458
|
|
|
459
|
+
let baseUnit = AMM_RESERVE_PRECISION;
|
|
460
|
+
if (market.amm.perLpBase == position.perLpBase) {
|
|
461
|
+
if (
|
|
462
|
+
position.perLpBase >= 0 &&
|
|
463
|
+
position.perLpBase <= AMM_RESERVE_PRECISION_EXP.toNumber()
|
|
464
|
+
) {
|
|
465
|
+
const marketPerLpRebase = new BN(10 ** market.amm.perLpBase);
|
|
466
|
+
baseUnit = baseUnit.mul(marketPerLpRebase);
|
|
467
|
+
} else if (
|
|
468
|
+
position.perLpBase < 0 &&
|
|
469
|
+
position.perLpBase >= -AMM_RESERVE_PRECISION_EXP.toNumber()
|
|
470
|
+
) {
|
|
471
|
+
const marketPerLpRebase = new BN(10 ** Math.abs(market.amm.perLpBase));
|
|
472
|
+
baseUnit = baseUnit.div(marketPerLpRebase);
|
|
473
|
+
} else {
|
|
474
|
+
throw 'cannot calc';
|
|
475
|
+
}
|
|
476
|
+
} else {
|
|
477
|
+
throw 'market.amm.perLpBase != position.perLpBase';
|
|
478
|
+
}
|
|
479
|
+
|
|
436
480
|
const deltaBaa = market.amm.baseAssetAmountPerLp
|
|
437
481
|
.sub(position.lastBaseAssetAmountPerLp)
|
|
438
482
|
.mul(nShares)
|
|
439
|
-
.div(
|
|
483
|
+
.div(baseUnit);
|
|
440
484
|
const deltaQaa = market.amm.quoteAssetAmountPerLp
|
|
441
485
|
.sub(position.lastQuoteAssetAmountPerLp)
|
|
442
486
|
.mul(nShares)
|
|
443
|
-
.div(
|
|
487
|
+
.div(baseUnit);
|
|
444
488
|
|
|
445
489
|
function sign(v: BN) {
|
|
446
490
|
return v.isNeg() ? new BN(-1) : new BN(1);
|
|
@@ -749,7 +793,7 @@ export class User {
|
|
|
749
793
|
|
|
750
794
|
positionUnrealizedPnl = positionUnrealizedPnl
|
|
751
795
|
.mul(quotePrice)
|
|
752
|
-
.div(
|
|
796
|
+
.div(PRICE_PRECISION);
|
|
753
797
|
|
|
754
798
|
if (withWeightMarginCategory !== undefined) {
|
|
755
799
|
if (positionUnrealizedPnl.gt(ZERO)) {
|
|
@@ -1196,116 +1240,157 @@ export class User {
|
|
|
1196
1240
|
return health;
|
|
1197
1241
|
}
|
|
1198
1242
|
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
* @returns : Precision QUOTE_PRECISION
|
|
1202
|
-
*/
|
|
1203
|
-
getTotalPerpPositionValue(
|
|
1243
|
+
calculateWeightedPerpPositionValue(
|
|
1244
|
+
perpPosition: PerpPosition,
|
|
1204
1245
|
marginCategory?: MarginCategory,
|
|
1205
1246
|
liquidationBuffer?: BN,
|
|
1206
1247
|
includeOpenOrders?: boolean,
|
|
1207
1248
|
strict = false
|
|
1208
1249
|
): BN {
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
perpPosition.marketIndex
|
|
1213
|
-
);
|
|
1250
|
+
const market = this.driftClient.getPerpMarketAccount(
|
|
1251
|
+
perpPosition.marketIndex
|
|
1252
|
+
);
|
|
1214
1253
|
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1254
|
+
if (perpPosition.lpShares.gt(ZERO)) {
|
|
1255
|
+
// is an lp, clone so we dont mutate the position
|
|
1256
|
+
perpPosition = this.getPerpPositionWithLPSettle(
|
|
1257
|
+
market.marketIndex,
|
|
1258
|
+
this.getClonedPosition(perpPosition),
|
|
1259
|
+
!!marginCategory
|
|
1260
|
+
)[0];
|
|
1261
|
+
}
|
|
1223
1262
|
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1263
|
+
let valuationPrice = this.getOracleDataForPerpMarket(
|
|
1264
|
+
market.marketIndex
|
|
1265
|
+
).price;
|
|
1227
1266
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1267
|
+
if (isVariant(market.status, 'settlement')) {
|
|
1268
|
+
valuationPrice = market.expiryPrice;
|
|
1269
|
+
}
|
|
1231
1270
|
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1271
|
+
const baseAssetAmount = includeOpenOrders
|
|
1272
|
+
? calculateWorstCaseBaseAssetAmount(perpPosition)
|
|
1273
|
+
: perpPosition.baseAssetAmount;
|
|
1235
1274
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
if (marginCategory) {
|
|
1242
|
-
let marginRatio = new BN(
|
|
1243
|
-
calculateMarketMarginRatio(
|
|
1244
|
-
market,
|
|
1245
|
-
baseAssetAmount.abs(),
|
|
1246
|
-
marginCategory
|
|
1247
|
-
)
|
|
1248
|
-
);
|
|
1275
|
+
let baseAssetValue = baseAssetAmount
|
|
1276
|
+
.abs()
|
|
1277
|
+
.mul(valuationPrice)
|
|
1278
|
+
.div(BASE_PRECISION);
|
|
1249
1279
|
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1280
|
+
if (marginCategory) {
|
|
1281
|
+
let marginRatio = new BN(
|
|
1282
|
+
calculateMarketMarginRatio(
|
|
1283
|
+
market,
|
|
1284
|
+
baseAssetAmount.abs(),
|
|
1285
|
+
marginCategory
|
|
1286
|
+
)
|
|
1287
|
+
);
|
|
1256
1288
|
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1289
|
+
if (marginCategory === 'Initial') {
|
|
1290
|
+
marginRatio = BN.max(
|
|
1291
|
+
marginRatio,
|
|
1292
|
+
new BN(this.getUserAccount().maxMarginRatio)
|
|
1293
|
+
);
|
|
1294
|
+
}
|
|
1260
1295
|
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1296
|
+
if (liquidationBuffer !== undefined) {
|
|
1297
|
+
marginRatio = marginRatio.add(liquidationBuffer);
|
|
1298
|
+
}
|
|
1264
1299
|
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1300
|
+
if (isVariant(market.status, 'settlement')) {
|
|
1301
|
+
marginRatio = ZERO;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
const quoteSpotMarket = this.driftClient.getSpotMarketAccount(
|
|
1305
|
+
market.quoteSpotMarketIndex
|
|
1306
|
+
);
|
|
1307
|
+
const quoteOraclePriceData = this.driftClient.getOraclePriceDataAndSlot(
|
|
1308
|
+
quoteSpotMarket.oracle
|
|
1309
|
+
).data;
|
|
1310
|
+
|
|
1311
|
+
let quotePrice;
|
|
1312
|
+
if (strict) {
|
|
1313
|
+
quotePrice = BN.max(
|
|
1314
|
+
quoteOraclePriceData.price,
|
|
1315
|
+
quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min
|
|
1316
|
+
);
|
|
1317
|
+
} else {
|
|
1318
|
+
quotePrice = quoteOraclePriceData.price;
|
|
1319
|
+
}
|
|
1282
1320
|
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1321
|
+
baseAssetValue = baseAssetValue
|
|
1322
|
+
.mul(quotePrice)
|
|
1323
|
+
.div(PRICE_PRECISION)
|
|
1324
|
+
.mul(marginRatio)
|
|
1325
|
+
.div(MARGIN_PRECISION);
|
|
1288
1326
|
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1327
|
+
if (includeOpenOrders) {
|
|
1328
|
+
baseAssetValue = baseAssetValue.add(
|
|
1329
|
+
new BN(perpPosition.openOrders).mul(OPEN_ORDER_MARGIN_REQUIREMENT)
|
|
1330
|
+
);
|
|
1293
1331
|
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
}
|
|
1306
|
-
}
|
|
1332
|
+
if (perpPosition.lpShares.gt(ZERO)) {
|
|
1333
|
+
baseAssetValue = baseAssetValue.add(
|
|
1334
|
+
BN.max(
|
|
1335
|
+
QUOTE_PRECISION,
|
|
1336
|
+
valuationPrice
|
|
1337
|
+
.mul(market.amm.orderStepSize)
|
|
1338
|
+
.mul(QUOTE_PRECISION)
|
|
1339
|
+
.div(AMM_RESERVE_PRECISION)
|
|
1340
|
+
.div(PRICE_PRECISION)
|
|
1341
|
+
)
|
|
1342
|
+
);
|
|
1307
1343
|
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
return baseAssetValue;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
/**
|
|
1351
|
+
* calculates position value of a single perp market in margin system
|
|
1352
|
+
* @returns : Precision QUOTE_PRECISION
|
|
1353
|
+
*/
|
|
1354
|
+
public getPerpMarketLiabilityValue(
|
|
1355
|
+
marketIndex: number,
|
|
1356
|
+
marginCategory?: MarginCategory,
|
|
1357
|
+
liquidationBuffer?: BN,
|
|
1358
|
+
includeOpenOrders?: boolean,
|
|
1359
|
+
strict = false
|
|
1360
|
+
): BN {
|
|
1361
|
+
const perpPosition = this.getPerpPosition(marketIndex);
|
|
1362
|
+
if (!perpPosition) {
|
|
1363
|
+
return ZERO;
|
|
1364
|
+
} else {
|
|
1365
|
+
return this.calculateWeightedPerpPositionValue(
|
|
1366
|
+
perpPosition,
|
|
1367
|
+
marginCategory,
|
|
1368
|
+
liquidationBuffer,
|
|
1369
|
+
includeOpenOrders,
|
|
1370
|
+
strict
|
|
1371
|
+
);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1308
1374
|
|
|
1375
|
+
/**
|
|
1376
|
+
* calculates sum of position value across all positions in margin system
|
|
1377
|
+
* @returns : Precision QUOTE_PRECISION
|
|
1378
|
+
*/
|
|
1379
|
+
getTotalPerpPositionValue(
|
|
1380
|
+
marginCategory?: MarginCategory,
|
|
1381
|
+
liquidationBuffer?: BN,
|
|
1382
|
+
includeOpenOrders?: boolean,
|
|
1383
|
+
strict = false
|
|
1384
|
+
): BN {
|
|
1385
|
+
return this.getActivePerpPositions().reduce(
|
|
1386
|
+
(totalPerpValue, perpPosition) => {
|
|
1387
|
+
const baseAssetValue = this.calculateWeightedPerpPositionValue(
|
|
1388
|
+
perpPosition,
|
|
1389
|
+
marginCategory,
|
|
1390
|
+
liquidationBuffer,
|
|
1391
|
+
includeOpenOrders,
|
|
1392
|
+
strict
|
|
1393
|
+
);
|
|
1309
1394
|
return totalPerpValue.add(baseAssetValue);
|
|
1310
1395
|
},
|
|
1311
1396
|
ZERO
|
|
@@ -2896,12 +2981,20 @@ export class User {
|
|
|
2896
2981
|
withdrawLimit = BN.max(withdrawLimit, userDepositAmount);
|
|
2897
2982
|
}
|
|
2898
2983
|
|
|
2899
|
-
const
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2984
|
+
const assetWeight = calculateAssetWeight(
|
|
2985
|
+
userDepositAmount,
|
|
2986
|
+
spotMarket,
|
|
2987
|
+
'Initial'
|
|
2988
|
+
);
|
|
2989
|
+
|
|
2990
|
+
const amountWithdrawable = assetWeight.eq(ZERO)
|
|
2991
|
+
? userDepositAmount
|
|
2992
|
+
: freeCollateral
|
|
2993
|
+
.mul(MARGIN_PRECISION)
|
|
2994
|
+
.div(assetWeight)
|
|
2995
|
+
.mul(PRICE_PRECISION)
|
|
2996
|
+
.div(oracleData.price)
|
|
2997
|
+
.mul(precisionIncrease);
|
|
2905
2998
|
|
|
2906
2999
|
const maxWithdrawValue = BN.min(
|
|
2907
3000
|
BN.min(amountWithdrawable, userDepositAmount),
|
|
@@ -3068,6 +3161,253 @@ export class User {
|
|
|
3068
3161
|
};
|
|
3069
3162
|
}
|
|
3070
3163
|
|
|
3164
|
+
public getHealthComponents({
|
|
3165
|
+
marginCategory,
|
|
3166
|
+
}: {
|
|
3167
|
+
marginCategory: MarginCategory;
|
|
3168
|
+
}): HealthComponents {
|
|
3169
|
+
const healthComponents: HealthComponents = {
|
|
3170
|
+
deposits: [],
|
|
3171
|
+
borrows: [],
|
|
3172
|
+
perpPositions: [],
|
|
3173
|
+
perpPnl: [],
|
|
3174
|
+
};
|
|
3175
|
+
|
|
3176
|
+
for (const perpPosition of this.getActivePerpPositions()) {
|
|
3177
|
+
const perpMarket = this.driftClient.getPerpMarketAccount(
|
|
3178
|
+
perpPosition.marketIndex
|
|
3179
|
+
);
|
|
3180
|
+
const oraclePriceData = this.driftClient.getOraclePriceDataAndSlot(
|
|
3181
|
+
perpMarket.amm.oracle
|
|
3182
|
+
).data;
|
|
3183
|
+
const oraclePrice = oraclePriceData.price;
|
|
3184
|
+
const worstCaseBaseAmount =
|
|
3185
|
+
calculateWorstCaseBaseAssetAmount(perpPosition);
|
|
3186
|
+
|
|
3187
|
+
const marginRatio = new BN(
|
|
3188
|
+
calculateMarketMarginRatio(
|
|
3189
|
+
perpMarket,
|
|
3190
|
+
worstCaseBaseAmount.abs(),
|
|
3191
|
+
marginCategory
|
|
3192
|
+
)
|
|
3193
|
+
);
|
|
3194
|
+
|
|
3195
|
+
const quoteSpotMarket = this.driftClient.getSpotMarketAccount(
|
|
3196
|
+
perpMarket.quoteSpotMarketIndex
|
|
3197
|
+
);
|
|
3198
|
+
const quoteOraclePriceData = this.driftClient.getOraclePriceDataAndSlot(
|
|
3199
|
+
quoteSpotMarket.oracle
|
|
3200
|
+
).data;
|
|
3201
|
+
|
|
3202
|
+
const baseAssetValue = worstCaseBaseAmount
|
|
3203
|
+
.abs()
|
|
3204
|
+
.mul(oraclePrice)
|
|
3205
|
+
.div(BASE_PRECISION);
|
|
3206
|
+
|
|
3207
|
+
let marginRequirement = baseAssetValue
|
|
3208
|
+
.mul(quoteOraclePriceData.price)
|
|
3209
|
+
.div(PRICE_PRECISION)
|
|
3210
|
+
.mul(marginRatio)
|
|
3211
|
+
.div(MARGIN_PRECISION);
|
|
3212
|
+
|
|
3213
|
+
marginRequirement = marginRequirement.add(
|
|
3214
|
+
new BN(perpPosition.openOrders).mul(OPEN_ORDER_MARGIN_REQUIREMENT)
|
|
3215
|
+
);
|
|
3216
|
+
|
|
3217
|
+
if (perpPosition.lpShares.gt(ZERO)) {
|
|
3218
|
+
marginRequirement = marginRequirement.add(
|
|
3219
|
+
BN.max(
|
|
3220
|
+
QUOTE_PRECISION,
|
|
3221
|
+
oraclePrice
|
|
3222
|
+
.mul(perpMarket.amm.orderStepSize)
|
|
3223
|
+
.mul(QUOTE_PRECISION)
|
|
3224
|
+
.div(AMM_RESERVE_PRECISION)
|
|
3225
|
+
.div(PRICE_PRECISION)
|
|
3226
|
+
)
|
|
3227
|
+
);
|
|
3228
|
+
}
|
|
3229
|
+
|
|
3230
|
+
healthComponents.perpPositions.push({
|
|
3231
|
+
marketIndex: perpMarket.marketIndex,
|
|
3232
|
+
size: worstCaseBaseAmount,
|
|
3233
|
+
value: baseAssetValue,
|
|
3234
|
+
weight: marginRatio,
|
|
3235
|
+
weightedValue: marginRequirement,
|
|
3236
|
+
});
|
|
3237
|
+
|
|
3238
|
+
const settledPerpPosition = this.getPerpPositionWithLPSettle(
|
|
3239
|
+
perpPosition.marketIndex,
|
|
3240
|
+
perpPosition
|
|
3241
|
+
)[0];
|
|
3242
|
+
|
|
3243
|
+
const positionUnrealizedPnl = calculatePositionPNL(
|
|
3244
|
+
perpMarket,
|
|
3245
|
+
settledPerpPosition,
|
|
3246
|
+
true,
|
|
3247
|
+
oraclePriceData
|
|
3248
|
+
);
|
|
3249
|
+
|
|
3250
|
+
let pnlWeight;
|
|
3251
|
+
if (positionUnrealizedPnl.gt(ZERO)) {
|
|
3252
|
+
pnlWeight = calculateUnrealizedAssetWeight(
|
|
3253
|
+
perpMarket,
|
|
3254
|
+
quoteSpotMarket,
|
|
3255
|
+
positionUnrealizedPnl,
|
|
3256
|
+
marginCategory,
|
|
3257
|
+
oraclePriceData
|
|
3258
|
+
);
|
|
3259
|
+
} else {
|
|
3260
|
+
pnlWeight = SPOT_MARKET_WEIGHT_PRECISION;
|
|
3261
|
+
}
|
|
3262
|
+
|
|
3263
|
+
const pnlValue = positionUnrealizedPnl
|
|
3264
|
+
.mul(quoteOraclePriceData.price)
|
|
3265
|
+
.div(PRICE_PRECISION);
|
|
3266
|
+
|
|
3267
|
+
const wegithedPnlValue = pnlValue
|
|
3268
|
+
.mul(pnlWeight)
|
|
3269
|
+
.div(SPOT_MARKET_WEIGHT_PRECISION);
|
|
3270
|
+
|
|
3271
|
+
healthComponents.perpPnl.push({
|
|
3272
|
+
marketIndex: perpMarket.marketIndex,
|
|
3273
|
+
size: positionUnrealizedPnl,
|
|
3274
|
+
value: pnlValue,
|
|
3275
|
+
weight: pnlWeight,
|
|
3276
|
+
weightedValue: wegithedPnlValue,
|
|
3277
|
+
});
|
|
3278
|
+
}
|
|
3279
|
+
|
|
3280
|
+
let netQuoteValue = ZERO;
|
|
3281
|
+
for (const spotPosition of this.getActiveSpotPositions()) {
|
|
3282
|
+
const spotMarketAccount: SpotMarketAccount =
|
|
3283
|
+
this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
|
|
3284
|
+
|
|
3285
|
+
const oraclePriceData = this.getOracleDataForSpotMarket(
|
|
3286
|
+
spotPosition.marketIndex
|
|
3287
|
+
);
|
|
3288
|
+
|
|
3289
|
+
if (spotPosition.marketIndex === QUOTE_SPOT_MARKET_INDEX) {
|
|
3290
|
+
const tokenAmount = getSignedTokenAmount(
|
|
3291
|
+
getTokenAmount(
|
|
3292
|
+
spotPosition.scaledBalance,
|
|
3293
|
+
spotMarketAccount,
|
|
3294
|
+
spotPosition.balanceType
|
|
3295
|
+
),
|
|
3296
|
+
spotPosition.balanceType
|
|
3297
|
+
);
|
|
3298
|
+
|
|
3299
|
+
netQuoteValue = netQuoteValue.add(tokenAmount);
|
|
3300
|
+
continue;
|
|
3301
|
+
}
|
|
3302
|
+
|
|
3303
|
+
const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] =
|
|
3304
|
+
getWorstCaseTokenAmounts(
|
|
3305
|
+
spotPosition,
|
|
3306
|
+
spotMarketAccount,
|
|
3307
|
+
oraclePriceData
|
|
3308
|
+
);
|
|
3309
|
+
|
|
3310
|
+
netQuoteValue = netQuoteValue.add(worstCaseQuoteTokenAmount);
|
|
3311
|
+
|
|
3312
|
+
const baseAssetValue = getTokenValue(
|
|
3313
|
+
worstCaseTokenAmount.abs(),
|
|
3314
|
+
spotMarketAccount.decimals,
|
|
3315
|
+
oraclePriceData
|
|
3316
|
+
);
|
|
3317
|
+
const isLiability = isVariant(spotPosition.balanceType, 'borrow');
|
|
3318
|
+
|
|
3319
|
+
let weight;
|
|
3320
|
+
if (isLiability) {
|
|
3321
|
+
weight = calculateLiabilityWeight(
|
|
3322
|
+
worstCaseTokenAmount.abs(),
|
|
3323
|
+
spotMarketAccount,
|
|
3324
|
+
marginCategory
|
|
3325
|
+
);
|
|
3326
|
+
} else {
|
|
3327
|
+
weight = calculateAssetWeight(
|
|
3328
|
+
worstCaseTokenAmount,
|
|
3329
|
+
spotMarketAccount,
|
|
3330
|
+
marginCategory
|
|
3331
|
+
);
|
|
3332
|
+
}
|
|
3333
|
+
|
|
3334
|
+
const weightedValue = baseAssetValue
|
|
3335
|
+
.mul(weight)
|
|
3336
|
+
.div(SPOT_MARKET_WEIGHT_PRECISION);
|
|
3337
|
+
|
|
3338
|
+
if (isLiability) {
|
|
3339
|
+
healthComponents.borrows.push({
|
|
3340
|
+
marketIndex: spotMarketAccount.marketIndex,
|
|
3341
|
+
size: worstCaseTokenAmount,
|
|
3342
|
+
value: baseAssetValue,
|
|
3343
|
+
weight: weight,
|
|
3344
|
+
weightedValue: weightedValue,
|
|
3345
|
+
});
|
|
3346
|
+
} else {
|
|
3347
|
+
healthComponents.deposits.push({
|
|
3348
|
+
marketIndex: spotMarketAccount.marketIndex,
|
|
3349
|
+
size: worstCaseTokenAmount,
|
|
3350
|
+
value: baseAssetValue,
|
|
3351
|
+
weight: weight,
|
|
3352
|
+
weightedValue: weightedValue,
|
|
3353
|
+
});
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
|
|
3357
|
+
if (!netQuoteValue.eq(ZERO)) {
|
|
3358
|
+
const spotMarketAccount = this.driftClient.getQuoteSpotMarketAccount();
|
|
3359
|
+
const oraclePriceData = this.getOracleDataForSpotMarket(
|
|
3360
|
+
QUOTE_SPOT_MARKET_INDEX
|
|
3361
|
+
);
|
|
3362
|
+
|
|
3363
|
+
const baseAssetValue = getTokenValue(
|
|
3364
|
+
netQuoteValue.abs(),
|
|
3365
|
+
spotMarketAccount.decimals,
|
|
3366
|
+
oraclePriceData
|
|
3367
|
+
);
|
|
3368
|
+
const isLiability = netQuoteValue.lt(ZERO);
|
|
3369
|
+
|
|
3370
|
+
let weight;
|
|
3371
|
+
if (isLiability) {
|
|
3372
|
+
weight = calculateLiabilityWeight(
|
|
3373
|
+
netQuoteValue.abs(),
|
|
3374
|
+
spotMarketAccount,
|
|
3375
|
+
marginCategory
|
|
3376
|
+
);
|
|
3377
|
+
} else {
|
|
3378
|
+
weight = calculateAssetWeight(
|
|
3379
|
+
netQuoteValue,
|
|
3380
|
+
spotMarketAccount,
|
|
3381
|
+
marginCategory
|
|
3382
|
+
);
|
|
3383
|
+
}
|
|
3384
|
+
|
|
3385
|
+
const weightedValue = baseAssetValue
|
|
3386
|
+
.mul(weight)
|
|
3387
|
+
.div(SPOT_MARKET_WEIGHT_PRECISION);
|
|
3388
|
+
|
|
3389
|
+
if (isLiability) {
|
|
3390
|
+
healthComponents.borrows.push({
|
|
3391
|
+
marketIndex: spotMarketAccount.marketIndex,
|
|
3392
|
+
size: netQuoteValue,
|
|
3393
|
+
value: baseAssetValue,
|
|
3394
|
+
weight: weight,
|
|
3395
|
+
weightedValue: weightedValue,
|
|
3396
|
+
});
|
|
3397
|
+
} else {
|
|
3398
|
+
healthComponents.deposits.push({
|
|
3399
|
+
marketIndex: spotMarketAccount.marketIndex,
|
|
3400
|
+
size: netQuoteValue,
|
|
3401
|
+
value: baseAssetValue,
|
|
3402
|
+
weight: weight,
|
|
3403
|
+
weightedValue: weightedValue,
|
|
3404
|
+
});
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3408
|
+
return healthComponents;
|
|
3409
|
+
}
|
|
3410
|
+
|
|
3071
3411
|
/**
|
|
3072
3412
|
* Get the total position value, excluding any position coming from the given target market
|
|
3073
3413
|
* @param marketToIgnore
|