@drift-labs/sdk 2.15.0-beta.0 → 2.16.0-beta.0

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/lib/user.js CHANGED
@@ -242,15 +242,15 @@ class User {
242
242
  * calculates Buying Power = free collateral / initial margin ratio
243
243
  * @returns : Precision QUOTE_PRECISION
244
244
  */
245
- getBuyingPower(marketIndex) {
245
+ getPerpBuyingPower(marketIndex) {
246
246
  const perpPosition = this.getPerpPosition(marketIndex);
247
247
  const worstCaseBaseAssetAmount = perpPosition
248
248
  ? margin_1.calculateWorstCaseBaseAssetAmount(perpPosition)
249
249
  : numericConstants_1.ZERO;
250
250
  const freeCollateral = this.getFreeCollateral();
251
- return this.getBuyingPowerFromFreeCollateralAndBaseAssetAmount(marketIndex, freeCollateral, worstCaseBaseAssetAmount);
251
+ return this.getPerpBuyingPowerFromFreeCollateralAndBaseAssetAmount(marketIndex, freeCollateral, worstCaseBaseAssetAmount);
252
252
  }
253
- getBuyingPowerFromFreeCollateralAndBaseAssetAmount(marketIndex, freeCollateral, baseAssetAmount) {
253
+ getPerpBuyingPowerFromFreeCollateralAndBaseAssetAmount(marketIndex, freeCollateral, baseAssetAmount) {
254
254
  const marginRatio = _1.calculateMarketMarginRatio(this.driftClient.getPerpMarketAccount(marketIndex), baseAssetAmount, 'Initial');
255
255
  return freeCollateral.mul(numericConstants_1.MARGIN_PRECISION).div(new _1.BN(marginRatio));
256
256
  }
@@ -325,16 +325,23 @@ class User {
325
325
  return pnl.add(_1.calculatePositionFundingPNL(market, perpPosition));
326
326
  }, numericConstants_1.ZERO);
327
327
  }
328
- getSpotMarketLiabilityValue(marketIndex, marginCategory, liquidationBuffer, includeOpenOrders, strict = false, now) {
328
+ getSpotMarketAssetAndLiabilityValue(marketIndex, marginCategory, liquidationBuffer, includeOpenOrders, strict = false, now) {
329
329
  now = now || new _1.BN(new Date().getTime() / 1000);
330
- return this.getUserAccount().spotPositions.reduce((totalLiabilityValue, spotPosition) => {
330
+ let netQuoteValue = numericConstants_1.ZERO;
331
+ let totalAssetValue = numericConstants_1.ZERO;
332
+ let totalLiabilityValue = numericConstants_1.ZERO;
333
+ for (const spotPosition of this.getUserAccount().spotPositions) {
334
+ const countForBase = marketIndex === undefined || spotPosition.marketIndex === marketIndex;
335
+ const countForQuote = marketIndex === undefined ||
336
+ marketIndex === numericConstants_1.QUOTE_SPOT_MARKET_INDEX ||
337
+ (includeOpenOrders && spotPosition.openOrders !== 0);
331
338
  if (spotPosition_1.isSpotPositionAvailable(spotPosition) ||
332
- (marketIndex !== undefined &&
333
- spotPosition.marketIndex !== marketIndex)) {
334
- return totalLiabilityValue;
339
+ (!countForBase && !countForQuote)) {
340
+ continue;
335
341
  }
336
342
  const spotMarketAccount = this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
337
- if (spotPosition.marketIndex === numericConstants_1.QUOTE_SPOT_MARKET_INDEX) {
343
+ if (spotPosition.marketIndex === numericConstants_1.QUOTE_SPOT_MARKET_INDEX &&
344
+ countForQuote) {
338
345
  if (types_1.isVariant(spotPosition.balanceType, 'borrow')) {
339
346
  const tokenAmount = spotBalance_1.getTokenAmount(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType);
340
347
  let weight = numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION;
@@ -344,31 +351,43 @@ class User {
344
351
  const weightedTokenValue = tokenAmount
345
352
  .mul(weight)
346
353
  .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
347
- return totalLiabilityValue.add(weightedTokenValue);
354
+ netQuoteValue = netQuoteValue.sub(weightedTokenValue);
355
+ continue;
348
356
  }
349
357
  else {
350
- return totalLiabilityValue;
358
+ const tokenAmount = spotBalance_1.getTokenAmount(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType);
359
+ netQuoteValue = netQuoteValue.add(tokenAmount);
360
+ continue;
351
361
  }
352
362
  }
353
363
  const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
354
- if (!includeOpenOrders) {
364
+ if (!includeOpenOrders && countForBase) {
355
365
  if (types_1.isVariant(spotPosition.balanceType, 'borrow')) {
356
366
  const tokenAmount = spotBalance_1.getTokenAmount(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType);
357
367
  const liabilityValue = this.getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer, strict, now);
358
- return totalLiabilityValue.add(liabilityValue);
368
+ totalLiabilityValue = totalLiabilityValue.add(liabilityValue);
369
+ continue;
359
370
  }
360
371
  else {
361
- return totalLiabilityValue;
372
+ const tokenAmount = spotBalance_1.getTokenAmount(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType);
373
+ const assetValue = this.getSpotAssetValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, strict, now);
374
+ totalAssetValue = totalAssetValue.add(assetValue);
375
+ continue;
362
376
  }
363
377
  }
364
378
  const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] = spotPosition_1.getWorstCaseTokenAmounts(spotPosition, spotMarketAccount, this.getOracleDataForSpotMarket(spotPosition.marketIndex));
365
- let newTotalLiabilityValue = totalLiabilityValue;
366
- if (worstCaseTokenAmount.lt(numericConstants_1.ZERO)) {
379
+ if (worstCaseTokenAmount.gt(numericConstants_1.ZERO) && countForBase) {
380
+ const baseAssetValue = this.getSpotAssetValue(worstCaseTokenAmount, oraclePriceData, spotMarketAccount, marginCategory, strict, now);
381
+ totalAssetValue = totalAssetValue.add(baseAssetValue);
382
+ }
383
+ if (worstCaseTokenAmount.lt(numericConstants_1.ZERO) && countForBase) {
367
384
  const baseLiabilityValue = this.getSpotLiabilityValue(worstCaseTokenAmount.abs(), oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer, strict, now);
368
- newTotalLiabilityValue =
369
- newTotalLiabilityValue.add(baseLiabilityValue);
385
+ totalLiabilityValue = totalLiabilityValue.add(baseLiabilityValue);
370
386
  }
371
- if (worstCaseQuoteTokenAmount.lt(numericConstants_1.ZERO)) {
387
+ if (worstCaseQuoteTokenAmount.gt(numericConstants_1.ZERO) && countForQuote) {
388
+ netQuoteValue = netQuoteValue.add(worstCaseQuoteTokenAmount);
389
+ }
390
+ if (worstCaseQuoteTokenAmount.lt(numericConstants_1.ZERO) && countForQuote) {
372
391
  let weight = numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION;
373
392
  if (marginCategory === 'Initial') {
374
393
  weight = _1.BN.max(weight, new _1.BN(this.getUserAccount().maxMarginRatio));
@@ -377,12 +396,21 @@ class User {
377
396
  .abs()
378
397
  .mul(weight)
379
398
  .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
380
- newTotalLiabilityValue =
381
- newTotalLiabilityValue.add(weightedTokenValue);
399
+ netQuoteValue = netQuoteValue.sub(weightedTokenValue);
382
400
  }
383
- newTotalLiabilityValue = newTotalLiabilityValue.add(new _1.BN(spotPosition.openOrders).mul(numericConstants_1.OPEN_ORDER_MARGIN_REQUIREMENT));
384
- return newTotalLiabilityValue;
385
- }, numericConstants_1.ZERO);
401
+ totalLiabilityValue = totalLiabilityValue.add(new _1.BN(spotPosition.openOrders).mul(numericConstants_1.OPEN_ORDER_MARGIN_REQUIREMENT));
402
+ }
403
+ if (netQuoteValue.gt(numericConstants_1.ZERO)) {
404
+ totalAssetValue = totalAssetValue.add(netQuoteValue);
405
+ }
406
+ else {
407
+ totalLiabilityValue = totalLiabilityValue.add(netQuoteValue.abs());
408
+ }
409
+ return { totalAssetValue, totalLiabilityValue };
410
+ }
411
+ getSpotMarketLiabilityValue(marketIndex, marginCategory, liquidationBuffer, includeOpenOrders, strict = false, now) {
412
+ const { totalLiabilityValue } = this.getSpotMarketAssetAndLiabilityValue(marketIndex, marginCategory, liquidationBuffer, includeOpenOrders, strict, now);
413
+ return totalLiabilityValue;
386
414
  }
387
415
  getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer, strict = false, now) {
388
416
  let liabilityValue = null;
@@ -409,46 +437,8 @@ class User {
409
437
  return liabilityValue;
410
438
  }
411
439
  getSpotMarketAssetValue(marketIndex, marginCategory, includeOpenOrders, strict = false, now) {
412
- now = now || new _1.BN(new Date().getTime() / 1000);
413
- return this.getUserAccount().spotPositions.reduce((totalAssetValue, spotPosition) => {
414
- if (spotPosition_1.isSpotPositionAvailable(spotPosition) ||
415
- (marketIndex !== undefined &&
416
- spotPosition.marketIndex !== marketIndex)) {
417
- return totalAssetValue;
418
- }
419
- // Todo this needs to account for whether it's based on initial or maintenance requirements
420
- const spotMarketAccount = this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
421
- if (spotPosition.marketIndex === numericConstants_1.QUOTE_SPOT_MARKET_INDEX) {
422
- if (types_1.isVariant(spotPosition.balanceType, 'deposit')) {
423
- const tokenAmount = spotBalance_1.getTokenAmount(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType);
424
- return totalAssetValue.add(tokenAmount);
425
- }
426
- else {
427
- return totalAssetValue;
428
- }
429
- }
430
- const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
431
- if (!includeOpenOrders) {
432
- if (types_1.isVariant(spotPosition.balanceType, 'deposit')) {
433
- const tokenAmount = spotBalance_1.getTokenAmount(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType);
434
- const assetValue = this.getSpotAssetValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, strict, now);
435
- return totalAssetValue.add(assetValue);
436
- }
437
- else {
438
- return totalAssetValue;
439
- }
440
- }
441
- const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] = spotPosition_1.getWorstCaseTokenAmounts(spotPosition, spotMarketAccount, this.getOracleDataForSpotMarket(spotPosition.marketIndex));
442
- let newTotalAssetValue = totalAssetValue;
443
- if (worstCaseTokenAmount.gt(numericConstants_1.ZERO)) {
444
- const baseAssetValue = this.getSpotAssetValue(worstCaseTokenAmount, oraclePriceData, spotMarketAccount, marginCategory, strict, now);
445
- newTotalAssetValue = newTotalAssetValue.add(baseAssetValue);
446
- }
447
- if (worstCaseQuoteTokenAmount.gt(numericConstants_1.ZERO)) {
448
- newTotalAssetValue = newTotalAssetValue.add(worstCaseQuoteTokenAmount);
449
- }
450
- return newTotalAssetValue;
451
- }, numericConstants_1.ZERO);
440
+ const { totalAssetValue } = this.getSpotMarketAssetAndLiabilityValue(marketIndex, marginCategory, undefined, includeOpenOrders, strict, now);
441
+ return totalAssetValue;
452
442
  }
453
443
  getSpotAssetValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, strict = false, now) {
454
444
  let assetValue = null;
@@ -466,8 +456,17 @@ class User {
466
456
  }
467
457
  return assetValue;
468
458
  }
459
+ getSpotTokenAmount(marketIndex) {
460
+ const spotPosition = this.getSpotPosition(marketIndex);
461
+ return spotBalance_1.getTokenAmount(spotPosition.scaledBalance, this.driftClient.getSpotMarketAccount(marketIndex), spotPosition.balanceType);
462
+ }
463
+ getSpotPositionValue(marketIndex, marginCategory, includeOpenOrders, strict = false, now) {
464
+ const { totalAssetValue, totalLiabilityValue } = this.getSpotMarketAssetAndLiabilityValue(marketIndex, marginCategory, undefined, includeOpenOrders, strict, now);
465
+ return totalAssetValue.sub(totalLiabilityValue);
466
+ }
469
467
  getNetSpotMarketValue(withWeightMarginCategory) {
470
- return this.getSpotMarketAssetValue(undefined, withWeightMarginCategory).sub(this.getSpotMarketLiabilityValue(undefined, withWeightMarginCategory));
468
+ const { totalAssetValue, totalLiabilityValue } = this.getSpotMarketAssetAndLiabilityValue(undefined, withWeightMarginCategory);
469
+ return totalAssetValue.sub(totalLiabilityValue);
471
470
  }
472
471
  /**
473
472
  * calculates TotalCollateral: collateral + unrealized pnl
@@ -625,16 +624,27 @@ class User {
625
624
  * @returns : Precision TEN_THOUSAND
626
625
  */
627
626
  getLeverage() {
628
- const totalPerpLiability = this.getTotalPerpPositionValue(undefined, undefined, true);
629
- const totalSpotLiability = this.getSpotMarketLiabilityValue(undefined, undefined, undefined, true);
630
- const totalLiabilityValue = totalPerpLiability.add(totalSpotLiability);
631
- const totalAssetValue = this.getTotalAssetValue();
632
- const netAssetValue = totalAssetValue.sub(totalSpotLiability);
627
+ // get leverage components
628
+ const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } = this.getLeverageComponents();
629
+ const totalLiabilityValue = perpLiabilityValue.add(spotLiabilityValue);
630
+ const totalAssetValue = spotAssetValue.add(perpPnl);
631
+ const netAssetValue = totalAssetValue.sub(spotLiabilityValue);
633
632
  if (netAssetValue.eq(numericConstants_1.ZERO)) {
634
633
  return numericConstants_1.ZERO;
635
634
  }
636
635
  return totalLiabilityValue.mul(numericConstants_1.TEN_THOUSAND).div(netAssetValue);
637
636
  }
637
+ getLeverageComponents() {
638
+ const perpLiability = this.getTotalPerpPositionValue(undefined, undefined, true);
639
+ const perpPnl = this.getUnrealizedPNL(true);
640
+ const { totalAssetValue: spotAssetValue, totalLiabilityValue: spotLiabilityValue, } = this.getSpotMarketAssetAndLiabilityValue(undefined, undefined, undefined, true);
641
+ return {
642
+ perpLiabilityValue: perpLiability,
643
+ perpPnl,
644
+ spotAssetValue,
645
+ spotLiabilityValue,
646
+ };
647
+ }
638
648
  getTotalLiabilityValue(marginCategory) {
639
649
  return this.getTotalPerpPositionValue(marginCategory, undefined, true).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, undefined, true));
640
650
  }
@@ -646,19 +656,16 @@ class User {
646
656
  * @params category {Initial, Maintenance}
647
657
  * @returns : Precision TEN_THOUSAND
648
658
  */
649
- getMaxLeverage(marketIndex, category = 'Initial') {
650
- const market = this.driftClient.getPerpMarketAccount(marketIndex);
651
- const totalPerpLiability = this.getTotalPerpPositionValue(undefined, undefined, true);
652
- const totalSpotLiability = this.getSpotMarketLiabilityValue(undefined, undefined, undefined, true);
653
- const totalAssetValue = this.getTotalAssetValue();
654
- const netAssetValue = totalAssetValue.sub(totalSpotLiability);
659
+ getMaxLeverageForPerp(perpMarketIndex, category = 'Initial') {
660
+ const market = this.driftClient.getPerpMarketAccount(perpMarketIndex);
661
+ const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } = this.getLeverageComponents();
662
+ const totalAssetValue = spotAssetValue.add(perpPnl);
663
+ const netAssetValue = totalAssetValue.sub(spotLiabilityValue);
655
664
  if (netAssetValue.eq(numericConstants_1.ZERO)) {
656
665
  return numericConstants_1.ZERO;
657
666
  }
658
- const totalLiabilityValue = totalPerpLiability.add(totalSpotLiability);
659
- const marginRatio = _1.calculateMarketMarginRatio(market,
660
- // worstCaseBaseAssetAmount.abs(),
661
- numericConstants_1.ZERO, // todo
667
+ const totalLiabilityValue = perpLiabilityValue.add(spotLiabilityValue);
668
+ const marginRatio = _1.calculateMarketMarginRatio(market, numericConstants_1.ZERO, // todo
662
669
  category);
663
670
  const freeCollateral = this.getFreeCollateral();
664
671
  // how much more liabilities can be opened w remaining free collateral
@@ -671,18 +678,60 @@ class User {
671
678
  .div(netAssetValue);
672
679
  }
673
680
  /**
674
- * calculates margin ratio: total collateral / |total position value|
681
+ * calculates max allowable leverage exceeding hitting requirement category
682
+ * @param spotMarketIndex
683
+ * @param direction
684
+ * @returns : Precision TEN_THOUSAND
685
+ */
686
+ getMaxLeverageForSpot(spotMarketIndex, direction) {
687
+ const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } = this.getLeverageComponents();
688
+ const totalLiabilityValue = perpLiabilityValue.add(spotLiabilityValue);
689
+ const totalAssetValue = spotAssetValue.add(perpPnl);
690
+ const netAssetValue = totalAssetValue.sub(spotLiabilityValue);
691
+ if (netAssetValue.eq(numericConstants_1.ZERO)) {
692
+ return numericConstants_1.ZERO;
693
+ }
694
+ const currentQuoteAssetValue = this.getSpotMarketAssetValue(numericConstants_1.QUOTE_SPOT_MARKET_INDEX);
695
+ const currentQuoteLiabilityValue = this.getSpotMarketLiabilityValue(numericConstants_1.QUOTE_SPOT_MARKET_INDEX);
696
+ const currentQuoteValue = currentQuoteAssetValue.sub(currentQuoteLiabilityValue);
697
+ const currentSpotMarketAssetValue = this.getSpotMarketAssetValue(spotMarketIndex);
698
+ const currentSpotMarketLiabilityValue = this.getSpotMarketLiabilityValue(spotMarketIndex);
699
+ const currentSpotMarketNetValue = currentSpotMarketAssetValue.sub(currentSpotMarketLiabilityValue);
700
+ const tradeQuoteAmount = this.getMaxTradeSizeUSDCForSpot(spotMarketIndex, direction, currentQuoteAssetValue, currentSpotMarketNetValue);
701
+ let assetValueToAdd = numericConstants_1.ZERO;
702
+ let liabilityValueToAdd = numericConstants_1.ZERO;
703
+ const newQuoteNetValue = types_1.isVariant(direction, 'short')
704
+ ? currentQuoteValue.add(tradeQuoteAmount)
705
+ : currentQuoteValue.sub(tradeQuoteAmount);
706
+ const newQuoteAssetValue = _1.BN.max(newQuoteNetValue, numericConstants_1.ZERO);
707
+ const newQuoteLiabilityValue = _1.BN.min(newQuoteNetValue, numericConstants_1.ZERO).abs();
708
+ assetValueToAdd = assetValueToAdd.add(newQuoteAssetValue.sub(currentQuoteAssetValue));
709
+ liabilityValueToAdd = liabilityValueToAdd.add(newQuoteLiabilityValue.sub(currentQuoteLiabilityValue));
710
+ const newSpotMarketNetValue = types_1.isVariant(direction, 'long')
711
+ ? currentSpotMarketNetValue.add(tradeQuoteAmount)
712
+ : currentSpotMarketNetValue.sub(tradeQuoteAmount);
713
+ const newSpotMarketAssetValue = _1.BN.max(newSpotMarketNetValue, numericConstants_1.ZERO);
714
+ const newSpotMarketLiabilityValue = _1.BN.min(newSpotMarketNetValue, numericConstants_1.ZERO).abs();
715
+ assetValueToAdd = assetValueToAdd.add(newSpotMarketAssetValue.sub(currentSpotMarketAssetValue));
716
+ liabilityValueToAdd = liabilityValueToAdd.add(newSpotMarketLiabilityValue.sub(currentSpotMarketLiabilityValue));
717
+ const finalTotalAssetValue = totalAssetValue.add(assetValueToAdd);
718
+ const finalTotalSpotLiability = spotLiabilityValue.add(liabilityValueToAdd);
719
+ const finalTotalLiabilityValue = totalLiabilityValue.add(liabilityValueToAdd);
720
+ const finalNetAssetValue = finalTotalAssetValue.sub(finalTotalSpotLiability);
721
+ return finalTotalLiabilityValue.mul(numericConstants_1.TEN_THOUSAND).div(finalNetAssetValue);
722
+ }
723
+ /**
724
+ * calculates margin ratio: 1 / leverage
675
725
  * @returns : Precision TEN_THOUSAND
676
726
  */
677
- getMarginRatio(marginCategory) {
678
- const totalPerpLiability = this.getTotalPerpPositionValue(undefined, undefined, true);
679
- const totalSpotLiability = this.getSpotMarketLiabilityValue(undefined, undefined, undefined, true);
680
- const totalLiabilityValue = totalPerpLiability.add(totalSpotLiability);
727
+ getMarginRatio() {
728
+ const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } = this.getLeverageComponents();
729
+ const totalLiabilityValue = perpLiabilityValue.add(spotLiabilityValue);
730
+ const totalAssetValue = spotAssetValue.add(perpPnl);
681
731
  if (totalLiabilityValue.eq(numericConstants_1.ZERO)) {
682
732
  return numericConstants_1.BN_MAX;
683
733
  }
684
- const totalAssetValue = this.getTotalAssetValue(marginCategory);
685
- const netAssetValue = totalAssetValue.sub(totalSpotLiability);
734
+ const netAssetValue = totalAssetValue.sub(spotLiabilityValue);
686
735
  return netAssetValue.mul(numericConstants_1.TEN_THOUSAND).div(totalLiabilityValue);
687
736
  }
688
737
  canBeLiquidated() {
@@ -728,7 +777,7 @@ class User {
728
777
  * @param marketIndex
729
778
  * @returns Precision : PRICE_PRECISION
730
779
  */
731
- spotLiquidationPrice(marketIndex) {
780
+ spotLiquidationPrice(marketIndex, positionBaseSizeChange = numericConstants_1.ZERO) {
732
781
  const currentSpotPosition = this.getSpotPosition(marketIndex);
733
782
  if (!currentSpotPosition) {
734
783
  return new _1.BN(-1);
@@ -737,7 +786,8 @@ class User {
737
786
  const maintenanceMarginRequirement = this.getMaintenanceMarginRequirement();
738
787
  const freeCollateral = _1.BN.max(numericConstants_1.ZERO, totalCollateral.sub(maintenanceMarginRequirement));
739
788
  const market = this.driftClient.getSpotMarketAccount(marketIndex);
740
- const signedTokenAmount = _1.getSignedTokenAmount(spotBalance_1.getTokenAmount(currentSpotPosition.scaledBalance, market, currentSpotPosition.balanceType), currentSpotPosition.balanceType);
789
+ let signedTokenAmount = _1.getSignedTokenAmount(spotBalance_1.getTokenAmount(currentSpotPosition.scaledBalance, market, currentSpotPosition.balanceType), currentSpotPosition.balanceType);
790
+ signedTokenAmount = signedTokenAmount.add(positionBaseSizeChange);
741
791
  if (signedTokenAmount.eq(numericConstants_1.ZERO)) {
742
792
  return new _1.BN(-1);
743
793
  }
@@ -894,7 +944,7 @@ class User {
894
944
  * @param tradeSide
895
945
  * @returns tradeSizeAllowed : Precision QUOTE_PRECISION
896
946
  */
897
- getMaxTradeSizeUSDC(targetMarketIndex, tradeSide) {
947
+ getMaxTradeSizeUSDCForPerp(targetMarketIndex, tradeSide) {
898
948
  const currentPosition = this.getPerpPosition(targetMarketIndex) ||
899
949
  this.getEmptyPosition(targetMarketIndex);
900
950
  const targetSide = types_1.isVariant(tradeSide, 'short') ? 'short' : 'long';
@@ -909,7 +959,7 @@ class User {
909
959
  const oppositeSizeValueUSDC = targetingSameSide
910
960
  ? numericConstants_1.ZERO
911
961
  : this.getPerpPositionValue(targetMarketIndex, oracleData);
912
- let maxPositionSize = this.getBuyingPower(targetMarketIndex);
962
+ let maxPositionSize = this.getPerpBuyingPower(targetMarketIndex);
913
963
  if (maxPositionSize.gte(numericConstants_1.ZERO)) {
914
964
  if (oppositeSizeValueUSDC.eq(numericConstants_1.ZERO)) {
915
965
  // case 1 : Regular trade where current total position less than max, and no opposite position to account for
@@ -936,7 +986,7 @@ class User {
936
986
  }
937
987
  else {
938
988
  const freeCollateralAfterClose = totalCollateral.sub(marginRequirementAfterClosing);
939
- const buyingPowerAfterClose = this.getBuyingPowerFromFreeCollateralAndBaseAssetAmount(targetMarketIndex, freeCollateralAfterClose, numericConstants_1.ZERO);
989
+ const buyingPowerAfterClose = this.getPerpBuyingPowerFromFreeCollateralAndBaseAssetAmount(targetMarketIndex, freeCollateralAfterClose, numericConstants_1.ZERO);
940
990
  maxPositionSize = perpPositionValue.add(buyingPowerAfterClose);
941
991
  }
942
992
  }
@@ -949,15 +999,99 @@ class User {
949
999
  const oneMilli = maxPositionSize.div(numericConstants_1.QUOTE_PRECISION);
950
1000
  return maxPositionSize.sub(oneMilli);
951
1001
  }
1002
+ /**
1003
+ * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
1004
+ *
1005
+ * @param targetMarketIndex
1006
+ * @param direction
1007
+ * @param currentQuoteAssetValue
1008
+ * @param currentSpotMarketNetValue
1009
+ * @returns tradeSizeAllowed : Precision QUOTE_PRECISION
1010
+ */
1011
+ getMaxTradeSizeUSDCForSpot(targetMarketIndex, direction, currentQuoteAssetValue, currentSpotMarketNetValue) {
1012
+ const market = this.driftClient.getSpotMarketAccount(targetMarketIndex);
1013
+ currentQuoteAssetValue = this.getSpotMarketAssetValue(numericConstants_1.QUOTE_SPOT_MARKET_INDEX);
1014
+ currentSpotMarketNetValue =
1015
+ currentSpotMarketNetValue !== null && currentSpotMarketNetValue !== void 0 ? currentSpotMarketNetValue : this.getSpotPositionValue(targetMarketIndex);
1016
+ let freeCollateral = this.getFreeCollateral();
1017
+ const marginRatio = _1.calculateSpotMarketMarginRatio(market, 'Initial', numericConstants_1.ZERO, types_1.isVariant(direction, 'long')
1018
+ ? _1.SpotBalanceType.DEPOSIT
1019
+ : _1.SpotBalanceType.BORROW);
1020
+ let tradeAmount = numericConstants_1.ZERO;
1021
+ if (this.getUserAccount().isMarginTradingEnabled) {
1022
+ // if the user is buying/selling and already short/long, need to account for closing out short/long
1023
+ if (types_1.isVariant(direction, 'long') && currentSpotMarketNetValue.lt(numericConstants_1.ZERO)) {
1024
+ tradeAmount = currentSpotMarketNetValue.abs();
1025
+ const marginRatio = _1.calculateSpotMarketMarginRatio(market, 'Initial', this.getSpotTokenAmount(targetMarketIndex), _1.SpotBalanceType.BORROW);
1026
+ freeCollateral = freeCollateral.add(tradeAmount.mul(new _1.BN(marginRatio)).div(numericConstants_1.MARGIN_PRECISION));
1027
+ }
1028
+ else if (types_1.isVariant(direction, 'short') &&
1029
+ currentSpotMarketNetValue.gt(numericConstants_1.ZERO)) {
1030
+ tradeAmount = currentSpotMarketNetValue;
1031
+ const marginRatio = _1.calculateSpotMarketMarginRatio(market, 'Initial', this.getSpotTokenAmount(targetMarketIndex), _1.SpotBalanceType.DEPOSIT);
1032
+ freeCollateral = freeCollateral.add(tradeAmount.mul(new _1.BN(marginRatio)).div(numericConstants_1.MARGIN_PRECISION));
1033
+ }
1034
+ tradeAmount = tradeAmount.add(freeCollateral.mul(numericConstants_1.MARGIN_PRECISION).div(new _1.BN(marginRatio)));
1035
+ }
1036
+ else if (types_1.isVariant(direction, 'long')) {
1037
+ tradeAmount = _1.BN.min(currentQuoteAssetValue, freeCollateral.mul(numericConstants_1.MARGIN_PRECISION).div(new _1.BN(marginRatio)));
1038
+ }
1039
+ else {
1040
+ tradeAmount = _1.BN.max(numericConstants_1.ZERO, currentSpotMarketNetValue);
1041
+ }
1042
+ return tradeAmount;
1043
+ }
952
1044
  // TODO - should this take the price impact of the trade into account for strict accuracy?
953
1045
  /**
954
1046
  * Returns the leverage ratio for the account after adding (or subtracting) the given quote size to the given position
955
1047
  * @param targetMarketIndex
956
- * @param positionMarketIndex
1048
+ * @param: targetMarketType
957
1049
  * @param tradeQuoteAmount
1050
+ * @param tradeSide
1051
+ * @param includeOpenOrders
958
1052
  * @returns leverageRatio : Precision TEN_THOUSAND
959
1053
  */
960
- accountLeverageRatioAfterTrade(targetMarketIndex, tradeQuoteAmount, tradeSide, includeOpenOrders = true) {
1054
+ accountLeverageRatioAfterTrade(targetMarketIndex, targetMarketType, tradeQuoteAmount, tradeSide, includeOpenOrders = true) {
1055
+ const tradeIsPerp = types_1.isVariant(targetMarketType, 'perp');
1056
+ if (!tradeIsPerp) {
1057
+ // calculate new asset/liability values for base and quote market to find new account leverage
1058
+ const totalLiabilityValue = this.getTotalLiabilityValue();
1059
+ const totalAssetValue = this.getTotalAssetValue();
1060
+ const spotLiabilityValue = this.getSpotMarketLiabilityValue(undefined, undefined, undefined, includeOpenOrders);
1061
+ const currentQuoteAssetValue = this.getSpotMarketAssetValue(numericConstants_1.QUOTE_SPOT_MARKET_INDEX, undefined, includeOpenOrders);
1062
+ const currentQuoteLiabilityValue = this.getSpotMarketLiabilityValue(numericConstants_1.QUOTE_SPOT_MARKET_INDEX, undefined, undefined, includeOpenOrders);
1063
+ const currentQuoteValue = currentQuoteAssetValue.sub(currentQuoteLiabilityValue);
1064
+ const currentSpotMarketAssetValue = this.getSpotMarketAssetValue(targetMarketIndex, undefined, includeOpenOrders);
1065
+ const currentSpotMarketLiabilityValue = this.getSpotMarketLiabilityValue(targetMarketIndex, undefined, undefined, includeOpenOrders);
1066
+ const currentSpotMarketNetValue = currentSpotMarketAssetValue.sub(currentSpotMarketLiabilityValue);
1067
+ let assetValueToAdd = numericConstants_1.ZERO;
1068
+ let liabilityValueToAdd = numericConstants_1.ZERO;
1069
+ const newQuoteNetValue = tradeSide == _1.PositionDirection.SHORT
1070
+ ? currentQuoteValue.add(tradeQuoteAmount)
1071
+ : currentQuoteValue.sub(tradeQuoteAmount);
1072
+ const newQuoteAssetValue = _1.BN.max(newQuoteNetValue, numericConstants_1.ZERO);
1073
+ const newQuoteLiabilityValue = _1.BN.min(newQuoteNetValue, numericConstants_1.ZERO).abs();
1074
+ assetValueToAdd = assetValueToAdd.add(newQuoteAssetValue.sub(currentQuoteAssetValue));
1075
+ liabilityValueToAdd = liabilityValueToAdd.add(newQuoteLiabilityValue.sub(currentQuoteLiabilityValue));
1076
+ const newSpotMarketNetValue = tradeSide == _1.PositionDirection.LONG
1077
+ ? currentSpotMarketNetValue.add(tradeQuoteAmount)
1078
+ : currentSpotMarketNetValue.sub(tradeQuoteAmount);
1079
+ const newSpotMarketAssetValue = _1.BN.max(newSpotMarketNetValue, numericConstants_1.ZERO);
1080
+ const newSpotMarketLiabilityValue = _1.BN.min(newSpotMarketNetValue, numericConstants_1.ZERO).abs();
1081
+ assetValueToAdd = assetValueToAdd.add(newSpotMarketAssetValue.sub(currentSpotMarketAssetValue));
1082
+ liabilityValueToAdd = liabilityValueToAdd.add(newSpotMarketLiabilityValue.sub(currentSpotMarketLiabilityValue));
1083
+ const totalAssetValueAfterTrade = totalAssetValue.add(assetValueToAdd);
1084
+ const totalSpotLiabilityValueAfterTrade = spotLiabilityValue.add(liabilityValueToAdd);
1085
+ const totalLiabilityValueAfterTrade = totalLiabilityValue.add(liabilityValueToAdd);
1086
+ const netAssetValueAfterTrade = totalAssetValueAfterTrade.sub(totalSpotLiabilityValueAfterTrade);
1087
+ if (netAssetValueAfterTrade.eq(numericConstants_1.ZERO)) {
1088
+ return numericConstants_1.ZERO;
1089
+ }
1090
+ const newLeverage = totalLiabilityValueAfterTrade
1091
+ .mul(numericConstants_1.TEN_THOUSAND)
1092
+ .div(netAssetValueAfterTrade);
1093
+ return newLeverage;
1094
+ }
961
1095
  const currentPosition = this.getPerpPosition(targetMarketIndex) ||
962
1096
  this.getEmptyPosition(targetMarketIndex);
963
1097
  const oracleData = this.getOracleDataForPerpMarket(targetMarketIndex);
@@ -1063,7 +1197,7 @@ class User {
1063
1197
  depositAmount: numericConstants_1.ZERO,
1064
1198
  };
1065
1199
  }
1066
- const depositAmount = spotBalance_1.getTokenAmount(position.scaledBalance, spotMarket, 'deposit');
1200
+ const depositAmount = spotBalance_1.getTokenAmount(position.scaledBalance, spotMarket, _1.SpotBalanceType.DEPOSIT);
1067
1201
  if (netDeposits.lt(numericConstants_1.ZERO)) {
1068
1202
  return {
1069
1203
  canBypass: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.15.0-beta.0",
3
+ "version": "2.16.0-beta.0",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -87,6 +87,16 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [
87
87
  launchTs: 1670347281000,
88
88
  oracleSource: OracleSource.PYTH,
89
89
  },
90
+ {
91
+ fullName: 'Aptos',
92
+ category: ['L1', 'Infra'],
93
+ symbol: 'APT-PERP',
94
+ baseAssetSymbol: 'APT',
95
+ marketIndex: 3,
96
+ oracle: new PublicKey('FNNvb1AFDnDVPkocEri8mWbJ1952HQZtFLuwPiUjSJQ'),
97
+ launchTs: 1675802661000,
98
+ oracleSource: OracleSource.PYTH,
99
+ },
90
100
  ];
91
101
 
92
102
  export const PerpMarkets: { [key in DriftEnv]: PerpMarketConfig[] } = {
@@ -61,15 +61,15 @@ export async function fetchLogs(
61
61
 
62
62
  const chunkedSignatures = chunk(filteredSignatures, 100);
63
63
 
64
+ const config = { commitment: finality, maxSupportedTransactionVersion: 0 };
65
+
64
66
  const transactionLogs = (
65
67
  await Promise.all(
66
68
  chunkedSignatures.map(async (chunk) => {
67
69
  const transactions = await connection.getTransactions(
68
70
  chunk.map((confirmedSignature) => confirmedSignature.signature),
69
- {
70
- commitment: finality,
71
- maxSupportedTransactionVersion: 0,
72
- }
71
+ //@ts-ignore
72
+ config
73
73
  );
74
74
 
75
75
  return transactions.reduce((logs, transaction) => {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.15.0-0",
2
+ "version": "2.16.0-beta.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -25,9 +25,11 @@ import {
25
25
  MARGIN_PRECISION,
26
26
  PRICE_TO_QUOTE_PRECISION,
27
27
  ZERO,
28
+ QUOTE_SPOT_MARKET_INDEX,
28
29
  } from '../constants/numericConstants';
29
30
  import { getTokenAmount } from './spotBalance';
30
31
  import { DLOB } from '../dlob/DLOB';
32
+ import { assert } from '../assert/assert';
31
33
 
32
34
  /**
33
35
  * Calculates market mark price
@@ -131,7 +133,7 @@ export function calculateMarketMarginRatio(
131
133
  ): number {
132
134
  let marginRatio;
133
135
  switch (marginCategory) {
134
- case 'Initial':
136
+ case 'Initial': {
135
137
  marginRatio = calculateSizePremiumLiabilityWeight(
136
138
  size,
137
139
  new BN(market.imfFactor),
@@ -139,7 +141,8 @@ export function calculateMarketMarginRatio(
139
141
  MARGIN_PRECISION
140
142
  ).toNumber();
141
143
  break;
142
- case 'Maintenance':
144
+ }
145
+ case 'Maintenance': {
143
146
  marginRatio = calculateSizePremiumLiabilityWeight(
144
147
  size,
145
148
  new BN(market.imfFactor),
@@ -147,6 +150,7 @@ export function calculateMarketMarginRatio(
147
150
  MARGIN_PRECISION
148
151
  ).toNumber();
149
152
  break;
153
+ }
150
154
  }
151
155
 
152
156
  return marginRatio;
@@ -202,6 +206,25 @@ export function calculateMarketAvailablePNL(
202
206
  );
203
207
  }
204
208
 
209
+ export function calculateMarketMaxAvailableInsurance(
210
+ perpMarket: PerpMarketAccount,
211
+ spotMarket: SpotMarketAccount
212
+ ): BN {
213
+ assert(spotMarket.marketIndex == QUOTE_SPOT_MARKET_INDEX);
214
+
215
+ // todo: insuranceFundAllocation technically not guaranteed to be in Insurance Fund
216
+ const insuranceFundAllocation =
217
+ perpMarket.insuranceClaim.quoteMaxInsurance.sub(
218
+ perpMarket.insuranceClaim.quoteSettledInsurance
219
+ );
220
+ const ammFeePool = getTokenAmount(
221
+ perpMarket.amm.feePool.scaledBalance,
222
+ spotMarket,
223
+ SpotBalanceType.DEPOSIT
224
+ );
225
+ return insuranceFundAllocation.add(ammFeePool);
226
+ }
227
+
205
228
  export function calculateNetUserPnl(
206
229
  perpMarket: PerpMarketAccount,
207
230
  oraclePriceData: OraclePriceData
@@ -146,27 +146,27 @@ export function calculateAssetWeight(
146
146
  }
147
147
 
148
148
  export function calculateLiabilityWeight(
149
- balanceAmount: BN,
149
+ size: BN,
150
150
  spotMarket: SpotMarketAccount,
151
151
  marginCategory: MarginCategory
152
152
  ): BN {
153
153
  const sizePrecision = TEN.pow(new BN(spotMarket.decimals));
154
154
  let sizeInAmmReservePrecision;
155
155
  if (sizePrecision.gt(AMM_RESERVE_PRECISION)) {
156
- sizeInAmmReservePrecision = balanceAmount.div(
156
+ sizeInAmmReservePrecision = size.div(
157
157
  sizePrecision.div(AMM_RESERVE_PRECISION)
158
158
  );
159
159
  } else {
160
- sizeInAmmReservePrecision = balanceAmount
160
+ sizeInAmmReservePrecision = size
161
161
  .mul(AMM_RESERVE_PRECISION)
162
162
  .div(sizePrecision);
163
163
  }
164
164
 
165
- let assetWeight;
165
+ let liabilityWeight;
166
166
 
167
167
  switch (marginCategory) {
168
168
  case 'Initial':
169
- assetWeight = calculateSizePremiumLiabilityWeight(
169
+ liabilityWeight = calculateSizePremiumLiabilityWeight(
170
170
  sizeInAmmReservePrecision,
171
171
  new BN(spotMarket.imfFactor),
172
172
  new BN(spotMarket.initialLiabilityWeight),
@@ -174,7 +174,7 @@ export function calculateLiabilityWeight(
174
174
  );
175
175
  break;
176
176
  case 'Maintenance':
177
- assetWeight = calculateSizePremiumLiabilityWeight(
177
+ liabilityWeight = calculateSizePremiumLiabilityWeight(
178
178
  sizeInAmmReservePrecision,
179
179
  new BN(spotMarket.imfFactor),
180
180
  new BN(spotMarket.maintenanceLiabilityWeight),
@@ -182,11 +182,11 @@ export function calculateLiabilityWeight(
182
182
  );
183
183
  break;
184
184
  default:
185
- assetWeight = spotMarket.initialLiabilityWeight;
185
+ liabilityWeight = spotMarket.initialLiabilityWeight;
186
186
  break;
187
187
  }
188
188
 
189
- return assetWeight;
189
+ return liabilityWeight;
190
190
  }
191
191
 
192
192
  export function calculateUtilization(bank: SpotMarketAccount): BN {