@drift-labs/sdk 0.2.0-master.31 → 0.2.0-master.33

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.
Files changed (99) hide show
  1. package/lib/accounts/bulkAccountLoader.js +2 -1
  2. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +2 -2
  3. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +8 -8
  4. package/lib/accounts/types.d.ts +1 -0
  5. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +2 -2
  6. package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +7 -7
  7. package/lib/addresses/marketAddresses.js +1 -1
  8. package/lib/addresses/pda.d.ts +4 -4
  9. package/lib/addresses/pda.js +23 -22
  10. package/lib/admin.d.ts +30 -32
  11. package/lib/admin.js +111 -119
  12. package/lib/clearingHouse.d.ts +43 -34
  13. package/lib/clearingHouse.js +353 -193
  14. package/lib/clearingHouseConfig.d.ts +2 -2
  15. package/lib/clearingHouseUser.d.ts +26 -5
  16. package/lib/clearingHouseUser.js +151 -51
  17. package/lib/config.d.ts +2 -0
  18. package/lib/config.js +5 -1
  19. package/lib/constants/numericConstants.d.ts +1 -0
  20. package/lib/constants/numericConstants.js +3 -2
  21. package/lib/dlob/DLOB.d.ts +19 -11
  22. package/lib/dlob/DLOB.js +208 -107
  23. package/lib/dlob/DLOBNode.js +2 -10
  24. package/lib/dlob/NodeList.d.ts +1 -0
  25. package/lib/dlob/NodeList.js +6 -1
  26. package/lib/events/eventSubscriber.d.ts +1 -0
  27. package/lib/events/eventSubscriber.js +11 -4
  28. package/lib/events/fetchLogs.d.ts +3 -1
  29. package/lib/events/fetchLogs.js +13 -5
  30. package/lib/events/pollingLogProvider.js +1 -1
  31. package/lib/events/types.d.ts +1 -1
  32. package/lib/events/webSocketLogProvider.js +1 -1
  33. package/lib/factory/bigNum.d.ts +5 -4
  34. package/lib/factory/bigNum.js +36 -6
  35. package/lib/idl/clearing_house.json +1527 -1242
  36. package/lib/index.d.ts +3 -0
  37. package/lib/index.js +3 -0
  38. package/lib/math/amm.js +9 -9
  39. package/lib/math/exchangeStatus.d.ts +4 -0
  40. package/lib/math/exchangeStatus.js +18 -0
  41. package/lib/math/funding.js +10 -10
  42. package/lib/math/margin.js +6 -1
  43. package/lib/math/market.js +9 -9
  44. package/lib/math/orders.d.ts +7 -3
  45. package/lib/math/orders.js +39 -31
  46. package/lib/math/repeg.js +3 -3
  47. package/lib/math/spotBalance.js +3 -3
  48. package/lib/math/spotPosition.js +2 -2
  49. package/lib/serum/serumFulfillmentConfigMap.d.ts +10 -0
  50. package/lib/serum/serumFulfillmentConfigMap.js +17 -0
  51. package/lib/serum/serumSubscriber.d.ts +4 -0
  52. package/lib/serum/serumSubscriber.js +16 -1
  53. package/lib/types.d.ts +70 -85
  54. package/lib/types.js +12 -11
  55. package/lib/userMap/userMap.d.ts +17 -1
  56. package/lib/userMap/userMap.js +12 -0
  57. package/lib/userName.d.ts +1 -0
  58. package/lib/userName.js +3 -2
  59. package/package.json +1 -1
  60. package/src/accounts/bulkAccountLoader.ts +5 -1
  61. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +9 -9
  62. package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +8 -8
  63. package/src/addresses/marketAddresses.ts +2 -2
  64. package/src/addresses/pda.ts +20 -20
  65. package/src/admin.ts +246 -221
  66. package/src/clearingHouse.ts +556 -236
  67. package/src/clearingHouseConfig.ts +2 -2
  68. package/src/clearingHouseUser.ts +237 -87
  69. package/src/config.ts +8 -1
  70. package/src/constants/numericConstants.ts +5 -1
  71. package/src/dlob/DLOB.ts +290 -120
  72. package/src/dlob/DLOBNode.ts +2 -14
  73. package/src/dlob/NodeList.ts +7 -1
  74. package/src/events/eventSubscriber.ts +18 -4
  75. package/src/events/fetchLogs.ts +20 -5
  76. package/src/events/pollingLogProvider.ts +1 -1
  77. package/src/events/types.ts +2 -1
  78. package/src/events/webSocketLogProvider.ts +1 -1
  79. package/src/factory/bigNum.ts +59 -6
  80. package/src/idl/clearing_house.json +1527 -1242
  81. package/src/idl/pyth.json +98 -2
  82. package/src/index.ts +3 -0
  83. package/src/math/amm.ts +9 -9
  84. package/src/math/exchangeStatus.ts +31 -0
  85. package/src/math/funding.ts +20 -10
  86. package/src/math/margin.ts +7 -1
  87. package/src/math/market.ts +9 -9
  88. package/src/math/orders.ts +44 -29
  89. package/src/math/repeg.ts +3 -3
  90. package/src/math/spotBalance.ts +4 -4
  91. package/src/math/spotPosition.ts +2 -2
  92. package/src/serum/serumFulfillmentConfigMap.ts +26 -0
  93. package/src/serum/serumSubscriber.ts +20 -1
  94. package/src/types.ts +75 -61
  95. package/src/userMap/userMap.ts +25 -1
  96. package/src/userName.ts +2 -1
  97. package/tests/bn/test.ts +22 -1
  98. package/tests/dlob/helpers.ts +252 -81
  99. package/tests/dlob/test.ts +1115 -215
@@ -11,8 +11,8 @@ export type ClearingHouseConfig = {
11
11
  accountSubscription?: ClearingHouseSubscriptionConfig;
12
12
  opts?: ConfirmOptions;
13
13
  txSenderConfig?: TxSenderConfig;
14
- userIds?: number[];
15
- activeUserId?: number;
14
+ subAccountIds?: number[];
15
+ activeSubAccountId?: number;
16
16
  perpMarketIndexes?: number[];
17
17
  spotMarketIndexes?: number[];
18
18
  oracleInfos?: OracleInfo[];
@@ -22,6 +22,7 @@ import {
22
22
  MARGIN_PRECISION,
23
23
  SPOT_MARKET_WEIGHT_PRECISION,
24
24
  QUOTE_SPOT_MARKET_INDEX,
25
+ TEN,
25
26
  } from './constants/numericConstants';
26
27
  import {
27
28
  UserAccountSubscriber,
@@ -40,12 +41,15 @@ import {
40
41
  BN,
41
42
  SpotMarketAccount,
42
43
  getTokenValue,
44
+ SpotBalanceType,
43
45
  } from '.';
44
46
  import {
45
47
  getTokenAmount,
46
48
  calculateAssetWeight,
47
49
  calculateLiabilityWeight,
50
+ calculateWithdrawLimit,
48
51
  } from './math/spotBalance';
52
+ import { calculateMarketOpenBidAsk } from './math/amm';
49
53
  import {
50
54
  calculateBaseAssetValueWithOracle,
51
55
  calculateWorstCaseBaseAssetAmount,
@@ -186,6 +190,50 @@ export class ClearingHouseUser {
186
190
  return userAccountRPCResponse.value !== null;
187
191
  }
188
192
 
193
+ /**
194
+ * calculates the total open bids/asks in a perp market (including lps)
195
+ * @returns : open bids
196
+ * @returns : open asks
197
+ */
198
+ public getPerpBidAsks(marketIndex: number): [BN, BN] {
199
+ const position = this.getUserPosition(marketIndex);
200
+
201
+ const [lpOpenBids, lpOpenAsks] = this.getLPBidAsks(marketIndex);
202
+
203
+ const totalOpenBids = lpOpenBids.add(position.openBids);
204
+ const totalOpenAsks = lpOpenAsks.sub(position.openAsks);
205
+
206
+ return [totalOpenBids, totalOpenAsks];
207
+ }
208
+
209
+ /**
210
+ * calculates the open bids and asks for an lp
211
+ * @returns : lp open bids
212
+ * @returns : lp open asks
213
+ */
214
+ public getLPBidAsks(marketIndex: number): [BN, BN] {
215
+ const position = this.getUserPosition(marketIndex);
216
+ if (position.lpShares.eq(ZERO)) {
217
+ return [ZERO, ZERO];
218
+ }
219
+
220
+ const market = this.clearingHouse.getPerpMarketAccount(marketIndex);
221
+ const [marketOpenBids, marketOpenAsks] = calculateMarketOpenBidAsk(
222
+ market.amm.baseAssetReserve,
223
+ market.amm.minBaseAssetReserve,
224
+ market.amm.maxBaseAssetReserve
225
+ );
226
+
227
+ const lpOpenBids = marketOpenBids
228
+ .mul(position.lpShares)
229
+ .div(market.amm.sqrtK);
230
+ const lpOpenAsks = marketOpenAsks
231
+ .mul(position.lpShares)
232
+ .div(market.amm.sqrtK);
233
+
234
+ return [lpOpenBids, lpOpenAsks];
235
+ }
236
+
189
237
  /**
190
238
  * calculates the market position if the lp position was settled
191
239
  * @returns : the settled userPosition
@@ -196,16 +244,20 @@ export class ClearingHouseUser {
196
244
  const _position = this.getUserPosition(marketIndex);
197
245
  const position = this.getClonedPosition(_position);
198
246
 
247
+ if (position.lpShares.eq(ZERO)) {
248
+ return [position, ZERO, ZERO];
249
+ }
250
+
199
251
  const market = this.clearingHouse.getPerpMarketAccount(
200
252
  position.marketIndex
201
253
  );
202
254
  const nShares = position.lpShares;
203
255
 
204
- const deltaBaa = market.amm.marketPositionPerLp.baseAssetAmount
256
+ const deltaBaa = market.amm.baseAssetAmountPerLp
205
257
  .sub(position.lastNetBaseAssetAmountPerLp)
206
258
  .mul(nShares)
207
259
  .div(AMM_RESERVE_PRECISION);
208
- const deltaQaa = market.amm.marketPositionPerLp.quoteAssetAmount
260
+ const deltaQaa = market.amm.quoteAssetAmountPerLp
209
261
  .sub(position.lastNetQuoteAssetAmountPerLp)
210
262
  .mul(nShares)
211
263
  .div(AMM_RESERVE_PRECISION);
@@ -217,7 +269,7 @@ export class ClearingHouseUser {
217
269
  return sign;
218
270
  }
219
271
 
220
- function standardize(amount, stepsize) {
272
+ function standardize(amount: BN, stepsize: BN) {
221
273
  const remainder = amount.abs().mod(stepsize).mul(sign(amount));
222
274
  const standardizedAmount = amount.sub(remainder);
223
275
  return [standardizedAmount, remainder];
@@ -225,18 +277,18 @@ export class ClearingHouseUser {
225
277
 
226
278
  const [standardizedBaa, remainderBaa] = standardize(
227
279
  deltaBaa,
228
- market.amm.baseAssetAmountStepSize
280
+ market.amm.orderStepSize
229
281
  );
230
282
 
231
283
  position.remainderBaseAssetAmount += remainderBaa.toNumber();
232
284
 
233
285
  if (
234
286
  Math.abs(position.remainderBaseAssetAmount) >
235
- market.amm.baseAssetAmountStepSize.toNumber()
287
+ market.amm.orderStepSize.toNumber()
236
288
  ) {
237
289
  const [newStandardizedBaa, newRemainderBaa] = standardize(
238
- position.remainderBaseAssetAmount,
239
- market.amm.baseAssetAmountStepSize
290
+ new BN(position.remainderBaseAssetAmount),
291
+ market.amm.orderStepSize
240
292
  );
241
293
  position.baseAssetAmount =
242
294
  position.baseAssetAmount.add(newStandardizedBaa);
@@ -364,7 +416,9 @@ export class ClearingHouseUser {
364
416
  const market = this.clearingHouse.getPerpMarketAccount(
365
417
  perpPosition.marketIndex
366
418
  );
367
- const oraclePriceData = this.getOracleDataForMarket(market.marketIndex);
419
+ const oraclePriceData = this.getOracleDataForPerpMarket(
420
+ market.marketIndex
421
+ );
368
422
 
369
423
  let positionUnrealizedPnl = calculatePositionPNL(
370
424
  market,
@@ -432,7 +486,7 @@ export class ClearingHouseUser {
432
486
  if (spotPosition.marketIndex === QUOTE_SPOT_MARKET_INDEX) {
433
487
  if (isVariant(spotPosition.balanceType, 'borrow')) {
434
488
  const tokenAmount = getTokenAmount(
435
- spotPosition.balance,
489
+ spotPosition.scaledBalance,
436
490
  spotMarketAccount,
437
491
  spotPosition.balanceType
438
492
  );
@@ -441,7 +495,7 @@ export class ClearingHouseUser {
441
495
  if (marginCategory === 'Initial') {
442
496
  weight = BN.max(
443
497
  weight,
444
- new BN(this.getUserAccount().customMarginRatio)
498
+ new BN(this.getUserAccount().maxMarginRatio)
445
499
  );
446
500
  }
447
501
 
@@ -462,7 +516,7 @@ export class ClearingHouseUser {
462
516
  if (!includeOpenOrders) {
463
517
  if (isVariant(spotPosition.balanceType, 'borrow')) {
464
518
  const tokenAmount = getTokenAmount(
465
- spotPosition.balance,
519
+ spotPosition.scaledBalance,
466
520
  spotMarketAccount,
467
521
  spotPosition.balanceType
468
522
  );
@@ -505,7 +559,7 @@ export class ClearingHouseUser {
505
559
  if (marginCategory === 'Initial') {
506
560
  weight = BN.max(
507
561
  weight,
508
- new BN(this.getUserAccount().customMarginRatio)
562
+ new BN(this.getUserAccount().maxMarginRatio)
509
563
  );
510
564
  }
511
565
 
@@ -545,10 +599,7 @@ export class ClearingHouseUser {
545
599
  );
546
600
 
547
601
  if (marginCategory === 'Initial') {
548
- weight = BN.max(
549
- weight,
550
- new BN(this.getUserAccount().customMarginRatio)
551
- );
602
+ weight = BN.max(weight, new BN(this.getUserAccount().maxMarginRatio));
552
603
  }
553
604
 
554
605
  if (liquidationBuffer !== undefined) {
@@ -585,7 +636,7 @@ export class ClearingHouseUser {
585
636
  if (spotPosition.marketIndex === QUOTE_SPOT_MARKET_INDEX) {
586
637
  if (isVariant(spotPosition.balanceType, 'deposit')) {
587
638
  const tokenAmount = getTokenAmount(
588
- spotPosition.balance,
639
+ spotPosition.scaledBalance,
589
640
  spotMarketAccount,
590
641
  spotPosition.balanceType
591
642
  );
@@ -603,7 +654,7 @@ export class ClearingHouseUser {
603
654
  if (!includeOpenOrders) {
604
655
  if (isVariant(spotPosition.balanceType, 'deposit')) {
605
656
  const tokenAmount = getTokenAmount(
606
- spotPosition.balance,
657
+ spotPosition.scaledBalance,
607
658
  spotMarketAccount,
608
659
  spotPosition.balanceType
609
660
  );
@@ -748,12 +799,12 @@ export class ClearingHouseUser {
748
799
  perpPosition.openBids = perpPosition.openBids.add(openBids);
749
800
  }
750
801
 
751
- let valuationPrice = this.getOracleDataForMarket(
802
+ let valuationPrice = this.getOracleDataForPerpMarket(
752
803
  market.marketIndex
753
804
  ).price;
754
805
 
755
806
  if (isVariant(market.status, 'settlement')) {
756
- valuationPrice = market.settlementPrice;
807
+ valuationPrice = market.expiryPrice;
757
808
  }
758
809
 
759
810
  const baseAssetAmount = includeOpenOrders
@@ -777,7 +828,7 @@ export class ClearingHouseUser {
777
828
  if (marginCategory === 'Initial') {
778
829
  marginRatio = BN.max(
779
830
  marginRatio,
780
- new BN(this.getUserAccount().customMarginRatio)
831
+ new BN(this.getUserAccount().maxMarginRatio)
781
832
  );
782
833
  }
783
834
 
@@ -785,6 +836,10 @@ export class ClearingHouseUser {
785
836
  marginRatio = marginRatio.add(liquidationBuffer);
786
837
  }
787
838
 
839
+ if (isVariant(market.status, 'settlement')) {
840
+ marginRatio = ZERO;
841
+ }
842
+
788
843
  baseAssetValue = baseAssetValue
789
844
  .mul(marginRatio)
790
845
  .div(MARGIN_PRECISION);
@@ -843,7 +898,9 @@ export class ClearingHouseUser {
843
898
 
844
899
  const entryPrice = calculateEntryPrice(position);
845
900
 
846
- const oraclePriceData = this.getOracleDataForMarket(position.marketIndex);
901
+ const oraclePriceData = this.getOracleDataForPerpMarket(
902
+ position.marketIndex
903
+ );
847
904
 
848
905
  if (amountToClose) {
849
906
  if (amountToClose.eq(ZERO)) {
@@ -894,25 +951,10 @@ export class ClearingHouseUser {
894
951
  * calculates current user leverage across all positions
895
952
  * @returns : Precision TEN_THOUSAND
896
953
  */
897
- public getLeverage(marginCategory?: MarginCategory): BN {
898
- const totalLiabilityValue = this.getTotalPerpPositionValue(
899
- marginCategory,
900
- undefined,
901
- true
902
- ).add(
903
- this.getSpotMarketLiabilityValue(
904
- undefined,
905
- marginCategory,
906
- undefined,
907
- true
908
- )
909
- );
954
+ public getLeverage(): BN {
955
+ const totalLiabilityValue = this.getTotalLiabilityValue();
910
956
 
911
- const totalAssetValue = this.getSpotMarketAssetValue(
912
- undefined,
913
- marginCategory,
914
- true
915
- ).add(this.getUnrealizedPNL(true, undefined, marginCategory));
957
+ const totalAssetValue = this.getTotalAssetValue();
916
958
 
917
959
  if (totalAssetValue.eq(ZERO) && totalLiabilityValue.eq(ZERO)) {
918
960
  return ZERO;
@@ -921,9 +963,21 @@ export class ClearingHouseUser {
921
963
  return totalLiabilityValue.mul(TEN_THOUSAND).div(totalAssetValue);
922
964
  }
923
965
 
966
+ getTotalLiabilityValue(): BN {
967
+ return this.getTotalPerpPositionValue(undefined, undefined, true).add(
968
+ this.getSpotMarketLiabilityValue(undefined, undefined, undefined, true)
969
+ );
970
+ }
971
+
972
+ getTotalAssetValue(): BN {
973
+ return this.getSpotMarketAssetValue(undefined, undefined, true).add(
974
+ this.getUnrealizedPNL(true, undefined, undefined)
975
+ );
976
+ }
977
+
924
978
  /**
925
979
  * calculates max allowable leverage exceeding hitting requirement category
926
- * @params category {Initial, Partial, Maintenance}
980
+ * @params category {Initial, Maintenance}
927
981
  * @returns : Precision TEN_THOUSAND
928
982
  */
929
983
  public getMaxLeverage(
@@ -932,45 +986,44 @@ export class ClearingHouseUser {
932
986
  ): BN {
933
987
  const market = this.clearingHouse.getPerpMarketAccount(marketIndex);
934
988
 
935
- const marginRatioCategory = calculateMarketMarginRatio(
989
+ const totalAssetValue = this.getTotalAssetValue();
990
+ if (totalAssetValue.eq(ZERO)) {
991
+ return ZERO;
992
+ }
993
+
994
+ const totalLiabilityValue = this.getTotalLiabilityValue();
995
+
996
+ const marginRatio = calculateMarketMarginRatio(
936
997
  market,
937
998
  // worstCaseBaseAssetAmount.abs(),
938
999
  ZERO, // todo
939
1000
  category
940
1001
  );
941
- const maxLeverage = TEN_THOUSAND.mul(TEN_THOUSAND).div(
942
- new BN(marginRatioCategory)
943
- );
944
- return maxLeverage;
1002
+ const freeCollateral = this.getFreeCollateral();
1003
+
1004
+ // how much more liabilities can be opened w remaining free collateral
1005
+ const additionalLiabilities = freeCollateral
1006
+ .mul(MARGIN_PRECISION)
1007
+ .div(new BN(marginRatio));
1008
+
1009
+ return totalLiabilityValue
1010
+ .add(additionalLiabilities)
1011
+ .mul(TEN_THOUSAND)
1012
+ .div(totalAssetValue);
945
1013
  }
946
1014
 
947
1015
  /**
948
1016
  * calculates margin ratio: total collateral / |total position value|
949
1017
  * @returns : Precision TEN_THOUSAND
950
1018
  */
951
- public getMarginRatio(marginCategory?: MarginCategory): BN {
952
- const totalLiabilityValue = this.getTotalPerpPositionValue(
953
- marginCategory,
954
- undefined,
955
- true
956
- ).add(
957
- this.getSpotMarketLiabilityValue(
958
- undefined,
959
- marginCategory,
960
- undefined,
961
- true
962
- )
963
- );
1019
+ public getMarginRatio(): BN {
1020
+ const totalLiabilityValue = this.getTotalLiabilityValue();
964
1021
 
965
1022
  if (totalLiabilityValue.eq(ZERO)) {
966
1023
  return BN_MAX;
967
1024
  }
968
1025
 
969
- const totalAssetValue = this.getSpotMarketAssetValue(
970
- undefined,
971
- marginCategory,
972
- true
973
- ).add(this.getUnrealizedPNL(true, undefined, marginCategory));
1026
+ const totalAssetValue = this.getTotalAssetValue();
974
1027
 
975
1028
  return totalAssetValue.mul(TEN_THOUSAND).div(totalLiabilityValue);
976
1029
  }
@@ -980,7 +1033,7 @@ export class ClearingHouseUser {
980
1033
 
981
1034
  // if user being liq'd, can continue to be liq'd until total collateral above the margin requirement plus buffer
982
1035
  let liquidationBuffer = undefined;
983
- if (this.getUserAccount().beingLiquidated) {
1036
+ if (this.getUserAccount().isBeingLiquidated) {
984
1037
  liquidationBuffer = new BN(
985
1038
  this.clearingHouse.getStateAccount().liquidationMarginBufferRatio
986
1039
  );
@@ -1084,7 +1137,7 @@ export class ClearingHouseUser {
1084
1137
  const proposedPerpPositionValue = calculateBaseAssetValueWithOracle(
1085
1138
  market,
1086
1139
  proposedPerpPosition,
1087
- this.getOracleDataForMarket(market.marketIndex)
1140
+ this.getOracleDataForPerpMarket(market.marketIndex)
1088
1141
  );
1089
1142
 
1090
1143
  // total position value after trade
@@ -1101,7 +1154,7 @@ export class ClearingHouseUser {
1101
1154
  const positionValue = calculateBaseAssetValueWithOracle(
1102
1155
  market,
1103
1156
  position,
1104
- this.getOracleDataForMarket(market.marketIndex)
1157
+ this.getOracleDataForPerpMarket(market.marketIndex)
1105
1158
  );
1106
1159
  const marketMarginRequirement = positionValue
1107
1160
  .mul(
@@ -1179,7 +1232,7 @@ export class ClearingHouseUser {
1179
1232
  if (positionBaseSizeChange.eq(ZERO)) {
1180
1233
  markPriceAfterTrade = calculateReservePrice(
1181
1234
  this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex),
1182
- this.getOracleDataForMarket(perpPosition.marketIndex)
1235
+ this.getOracleDataForPerpMarket(perpPosition.marketIndex)
1183
1236
  );
1184
1237
  } else {
1185
1238
  const direction = positionBaseSizeChange.gt(ZERO)
@@ -1190,7 +1243,7 @@ export class ClearingHouseUser {
1190
1243
  positionBaseSizeChange.abs(),
1191
1244
  this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex),
1192
1245
  'base',
1193
- this.getOracleDataForMarket(perpPosition.marketIndex)
1246
+ this.getOracleDataForPerpMarket(perpPosition.marketIndex)
1194
1247
  )[3]; // newPrice after swap
1195
1248
  }
1196
1249
 
@@ -1272,7 +1325,7 @@ export class ClearingHouseUser {
1272
1325
  ? true
1273
1326
  : targetSide === currentPositionSide;
1274
1327
 
1275
- const oracleData = this.getOracleDataForMarket(targetMarketIndex);
1328
+ const oracleData = this.getOracleDataForPerpMarket(targetMarketIndex);
1276
1329
 
1277
1330
  // add any position we have on the opposite side of the current trade, because we can "flip" the size of this position without taking any extra leverage.
1278
1331
  const oppositeSizeValueUSDC = targetingSameSide
@@ -1342,13 +1395,14 @@ export class ClearingHouseUser {
1342
1395
  public accountLeverageRatioAfterTrade(
1343
1396
  targetMarketIndex: number,
1344
1397
  tradeQuoteAmount: BN,
1345
- tradeSide: PositionDirection
1398
+ tradeSide: PositionDirection,
1399
+ includeOpenOrders = true
1346
1400
  ): BN {
1347
1401
  const currentPosition =
1348
1402
  this.getUserPosition(targetMarketIndex) ||
1349
1403
  this.getEmptyPosition(targetMarketIndex);
1350
1404
 
1351
- const oracleData = this.getOracleDataForMarket(targetMarketIndex);
1405
+ const oracleData = this.getOracleDataForPerpMarket(targetMarketIndex);
1352
1406
 
1353
1407
  let currentPositionQuoteAmount = this.getPerpPositionValue(
1354
1408
  targetMarketIndex,
@@ -1371,19 +1425,32 @@ export class ClearingHouseUser {
1371
1425
  .abs();
1372
1426
 
1373
1427
  const totalPositionAfterTradeExcludingTargetMarket =
1374
- this.getTotalPerpPositionValueExcludingMarket(targetMarketIndex);
1428
+ this.getTotalPerpPositionValueExcludingMarket(
1429
+ targetMarketIndex,
1430
+ undefined,
1431
+ undefined,
1432
+ includeOpenOrders
1433
+ );
1375
1434
 
1376
- const totalCollateral = this.getTotalCollateral();
1377
- if (totalCollateral.gt(ZERO)) {
1378
- const newLeverage = currentPerpPositionAfterTrade
1379
- .add(totalPositionAfterTradeExcludingTargetMarket)
1380
- .abs()
1381
- .mul(TEN_THOUSAND)
1382
- .div(totalCollateral);
1383
- return newLeverage;
1384
- } else {
1385
- return new BN(0);
1435
+ const totalAssetValue = this.getTotalAssetValue();
1436
+
1437
+ const totalPerpPositionValue = currentPerpPositionAfterTrade
1438
+ .add(totalPositionAfterTradeExcludingTargetMarket)
1439
+ .abs();
1440
+
1441
+ const totalLiabilitiesAfterTrade = totalPerpPositionValue.add(
1442
+ this.getSpotMarketLiabilityValue(undefined, undefined, undefined, false)
1443
+ );
1444
+
1445
+ if (totalAssetValue.eq(ZERO) && totalLiabilitiesAfterTrade.eq(ZERO)) {
1446
+ return ZERO;
1386
1447
  }
1448
+
1449
+ const newLeverage = totalLiabilitiesAfterTrade
1450
+ .mul(TEN_THOUSAND)
1451
+ .div(totalAssetValue);
1452
+
1453
+ return newLeverage;
1387
1454
  }
1388
1455
 
1389
1456
  /**
@@ -1399,17 +1466,96 @@ export class ClearingHouseUser {
1399
1466
  .div(new BN(feeTier.feeDenominator));
1400
1467
  }
1401
1468
 
1469
+ /**
1470
+ * Calculates a user's max withdrawal amounts for a spot market. If reduceOnly is true,
1471
+ * it will return the max withdrawal amount without opening a liability for the user
1472
+ * @param marketIndex
1473
+ * @returns withdrawalLimit : Precision is the token precision for the chosen SpotMarket
1474
+ */
1475
+ public getWithdrawalLimit(marketIndex: number, reduceOnly?: boolean): BN {
1476
+ const nowTs = new BN(Math.floor(Date.now() / 1000));
1477
+ const spotMarket = this.clearingHouse.getSpotMarketAccount(marketIndex);
1478
+
1479
+ const { borrowLimit, withdrawLimit } = calculateWithdrawLimit(
1480
+ spotMarket,
1481
+ nowTs
1482
+ );
1483
+
1484
+ const freeCollateral = this.getFreeCollateral();
1485
+ const oracleData = this.getOracleDataForSpotMarket(marketIndex);
1486
+ const precisionIncrease = TEN.pow(new BN(spotMarket.decimals - 6));
1487
+
1488
+ const amountWithdrawable = freeCollateral
1489
+ .mul(MARGIN_PRECISION)
1490
+ .div(spotMarket.initialAssetWeight)
1491
+ .mul(PRICE_PRECISION)
1492
+ .div(oracleData.price)
1493
+ .mul(precisionIncrease);
1494
+
1495
+ const userSpotPosition = this.getUserAccount().spotPositions.find(
1496
+ (spotPosition) =>
1497
+ isVariant(spotPosition.balanceType, 'deposit') &&
1498
+ spotPosition.marketIndex == marketIndex
1499
+ );
1500
+
1501
+ const userSpotBalance = userSpotPosition
1502
+ ? getTokenAmount(
1503
+ userSpotPosition.scaledBalance,
1504
+ this.clearingHouse.getSpotMarketAccount(marketIndex),
1505
+ SpotBalanceType.DEPOSIT
1506
+ )
1507
+ : ZERO;
1508
+
1509
+ const maxWithdrawValue = BN.min(
1510
+ BN.min(amountWithdrawable, userSpotBalance),
1511
+ withdrawLimit.abs()
1512
+ );
1513
+
1514
+ if (reduceOnly) {
1515
+ return BN.max(maxWithdrawValue, ZERO);
1516
+ } else {
1517
+ const weightedAssetValue = this.getSpotMarketAssetValue(
1518
+ marketIndex,
1519
+ 'Initial',
1520
+ false
1521
+ );
1522
+
1523
+ const freeCollatAfterWithdraw = userSpotBalance.gt(ZERO)
1524
+ ? freeCollateral.sub(weightedAssetValue)
1525
+ : freeCollateral;
1526
+
1527
+ const maxLiabilityAllowed = freeCollatAfterWithdraw
1528
+ .mul(MARGIN_PRECISION)
1529
+ .div(spotMarket.initialLiabilityWeight)
1530
+ .mul(PRICE_PRECISION)
1531
+ .div(oracleData.price)
1532
+ .mul(precisionIncrease);
1533
+
1534
+ const maxBorrowValue = BN.min(
1535
+ maxWithdrawValue.add(maxLiabilityAllowed),
1536
+ borrowLimit.abs()
1537
+ );
1538
+
1539
+ return BN.max(maxBorrowValue, ZERO);
1540
+ }
1541
+ }
1542
+
1402
1543
  /**
1403
1544
  * Get the total position value, excluding any position coming from the given target market
1404
1545
  * @param marketToIgnore
1405
1546
  * @returns positionValue : Precision QUOTE_PRECISION
1406
1547
  */
1407
- private getTotalPerpPositionValueExcludingMarket(marketToIgnore: number): BN {
1548
+ private getTotalPerpPositionValueExcludingMarket(
1549
+ marketToIgnore: number,
1550
+ marginCategory?: MarginCategory,
1551
+ liquidationBuffer?: BN,
1552
+ includeOpenOrders?: boolean
1553
+ ): BN {
1408
1554
  const currentPerpPosition =
1409
1555
  this.getUserPosition(marketToIgnore) ||
1410
1556
  this.getEmptyPosition(marketToIgnore);
1411
1557
 
1412
- const oracleData = this.getOracleDataForMarket(marketToIgnore);
1558
+ const oracleData = this.getOracleDataForPerpMarket(marketToIgnore);
1413
1559
 
1414
1560
  let currentPerpPositionValueUSDC = ZERO;
1415
1561
  if (currentPerpPosition) {
@@ -1419,10 +1565,14 @@ export class ClearingHouseUser {
1419
1565
  );
1420
1566
  }
1421
1567
 
1422
- return this.getTotalPerpPositionValue().sub(currentPerpPositionValueUSDC);
1568
+ return this.getTotalPerpPositionValue(
1569
+ marginCategory,
1570
+ liquidationBuffer,
1571
+ includeOpenOrders
1572
+ ).sub(currentPerpPositionValueUSDC);
1423
1573
  }
1424
1574
 
1425
- private getOracleDataForMarket(marketIndex: number): OraclePriceData {
1575
+ private getOracleDataForPerpMarket(marketIndex: number): OraclePriceData {
1426
1576
  const oracleKey =
1427
1577
  this.clearingHouse.getPerpMarketAccount(marketIndex).amm.oracle;
1428
1578
  const oracleData =
package/src/config.ts CHANGED
@@ -17,6 +17,8 @@ type DriftConfig = {
17
17
  PYTH_ORACLE_MAPPING_ADDRESS: string;
18
18
  CLEARING_HOUSE_PROGRAM_ID: string;
19
19
  USDC_MINT_ADDRESS: string;
20
+ SERUM_V3: string;
21
+ V2_ALPHA_TICKET_MINT_ADDRESS: string;
20
22
  PERP_MARKETS: PerpMarketConfig[];
21
23
  SPOT_MARKETS: SpotMarketConfig[];
22
24
  };
@@ -27,8 +29,11 @@ export const configs: { [key in DriftEnv]: DriftConfig } = {
27
29
  devnet: {
28
30
  ENV: 'devnet',
29
31
  PYTH_ORACLE_MAPPING_ADDRESS: 'BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2',
30
- CLEARING_HOUSE_PROGRAM_ID: 'DUZwKJKAk2C9S88BYvQzck1M1i5hySQjxB4zW6tJ29Nw',
32
+ CLEARING_HOUSE_PROGRAM_ID: 'FPVkWvcQgc4aCS8TrKo5c8GqTbKMauvzpADdqzRT2WbT',
31
33
  USDC_MINT_ADDRESS: '8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2',
34
+ SERUM_V3: 'DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY',
35
+ V2_ALPHA_TICKET_MINT_ADDRESS:
36
+ 'DeEiGWfCMP9psnLGkxGrBBMEAW5Jv8bBGMN8DCtFRCyB',
32
37
  PERP_MARKETS: DevnetPerpMarkets,
33
38
  SPOT_MARKETS: DevnetSpotMarkets,
34
39
  },
@@ -37,6 +42,8 @@ export const configs: { [key in DriftEnv]: DriftConfig } = {
37
42
  PYTH_ORACLE_MAPPING_ADDRESS: 'AHtgzX45WTKfkPG53L6WYhGEXwQkN1BVknET3sVsLL8J',
38
43
  CLEARING_HOUSE_PROGRAM_ID: 'dammHkt7jmytvbS3nHTxQNEcP59aE57nxwV21YdqEDN',
39
44
  USDC_MINT_ADDRESS: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
45
+ SERUM_V3: '9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin',
46
+ V2_ALPHA_TICKET_MINT_ADDRESS: '',
40
47
  PERP_MARKETS: MainnetMarkets,
41
48
  SPOT_MARKETS: MainnetSpotMarkets,
42
49
  },
@@ -37,7 +37,11 @@ export const SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION = new BN(10).pow(
37
37
  SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION_EXP
38
38
  );
39
39
 
40
- export const SPOT_MARKET_UTILIZATION_PRECISION = new BN(1000000);
40
+ export const SPOT_MARKET_UTILIZATION_PRECISION_EXP = new BN(6);
41
+ export const SPOT_MARKET_UTILIZATION_PRECISION = new BN(10).pow(
42
+ SPOT_MARKET_UTILIZATION_PRECISION_EXP
43
+ );
44
+
41
45
  export const SPOT_MARKET_WEIGHT_PRECISION = new BN(10000);
42
46
  export const SPOT_MARKET_BALANCE_PRECISION_EXP = new BN(9);
43
47
  export const SPOT_MARKET_BALANCE_PRECISION = new BN(10).pow(