@drift-labs/sdk 0.2.0-master.26 → 0.2.0-master.28

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 (143) hide show
  1. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +1 -0
  2. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +3 -0
  3. package/lib/accounts/types.d.ts +2 -0
  4. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +1 -0
  5. package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +3 -0
  6. package/lib/admin.d.ts +5 -2
  7. package/lib/admin.js +30 -4
  8. package/lib/clearingHouse.d.ts +3 -0
  9. package/lib/clearingHouse.js +19 -1
  10. package/lib/clearingHouseUser.d.ts +11 -9
  11. package/lib/clearingHouseUser.js +184 -108
  12. package/lib/config.js +1 -1
  13. package/lib/dlob/DLOB.d.ts +73 -0
  14. package/lib/dlob/DLOB.js +557 -0
  15. package/lib/dlob/DLOBNode.d.ts +52 -0
  16. package/lib/dlob/DLOBNode.js +82 -0
  17. package/lib/dlob/NodeList.d.ts +26 -0
  18. package/lib/dlob/NodeList.js +138 -0
  19. package/lib/idl/clearing_house.json +123 -203
  20. package/lib/index.d.ts +5 -0
  21. package/lib/index.js +5 -0
  22. package/lib/math/market.d.ts +3 -2
  23. package/lib/math/market.js +17 -12
  24. package/lib/math/orders.d.ts +3 -3
  25. package/lib/math/orders.js +31 -16
  26. package/lib/math/position.d.ts +3 -3
  27. package/lib/math/position.js +23 -16
  28. package/lib/math/repeg.js +8 -0
  29. package/lib/math/spotBalance.d.ts +3 -0
  30. package/lib/math/spotBalance.js +18 -1
  31. package/lib/math/spotPosition.d.ts +5 -1
  32. package/lib/math/spotPosition.js +16 -1
  33. package/lib/types.d.ts +25 -24
  34. package/lib/types.js +9 -3
  35. package/lib/userMap/userMap.d.ts +25 -0
  36. package/lib/userMap/userMap.js +73 -0
  37. package/lib/userMap/userStatsMap.d.ts +19 -0
  38. package/lib/userMap/userStatsMap.js +68 -0
  39. package/package.json +6 -3
  40. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +4 -0
  41. package/src/accounts/types.ts +1 -0
  42. package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +6 -0
  43. package/src/admin.ts +50 -8
  44. package/src/clearingHouse.ts +30 -1
  45. package/src/clearingHouseUser.ts +366 -167
  46. package/src/config.ts +1 -1
  47. package/src/dlob/DLOB.ts +884 -0
  48. package/src/dlob/DLOBNode.ts +163 -0
  49. package/src/dlob/NodeList.ts +185 -0
  50. package/src/idl/clearing_house.json +123 -203
  51. package/src/index.ts +5 -0
  52. package/src/math/market.ts +22 -13
  53. package/src/math/orders.ts +29 -21
  54. package/src/math/position.ts +36 -22
  55. package/src/math/repeg.ts +9 -0
  56. package/src/math/spotBalance.ts +26 -0
  57. package/src/math/spotPosition.ts +42 -1
  58. package/src/types.ts +26 -24
  59. package/src/userMap/userMap.ts +100 -0
  60. package/src/userMap/userStatsMap.ts +110 -0
  61. package/tests/bn/test.ts +2 -3
  62. package/tests/dlob/helpers.ts +322 -0
  63. package/tests/dlob/test.ts +2865 -0
  64. package/my-script/.env +0 -7
  65. package/my-script/getUserStats.ts +0 -106
  66. package/my-script/multiConnections.ts +0 -119
  67. package/my-script/test-regex.ts +0 -11
  68. package/my-script/utils.ts +0 -52
  69. package/src/accounts/bulkAccountLoader.js +0 -249
  70. package/src/accounts/bulkUserStatsSubscription.js +0 -75
  71. package/src/accounts/bulkUserSubscription.js +0 -75
  72. package/src/accounts/fetch.js +0 -92
  73. package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -465
  74. package/src/accounts/pollingOracleSubscriber.js +0 -156
  75. package/src/accounts/pollingTokenAccountSubscriber.js +0 -141
  76. package/src/accounts/pollingUserAccountSubscriber.js +0 -208
  77. package/src/accounts/pollingUserStatsAccountSubscriber.js +0 -208
  78. package/src/accounts/types.js +0 -28
  79. package/src/accounts/utils.js +0 -7
  80. package/src/accounts/webSocketAccountSubscriber.js +0 -138
  81. package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -433
  82. package/src/accounts/webSocketUserAccountSubscriber.js +0 -113
  83. package/src/accounts/webSocketUserStatsAccountSubsriber.js +0 -113
  84. package/src/addresses/pda.js +0 -186
  85. package/src/admin.js +0 -1284
  86. package/src/assert/assert.js +0 -9
  87. package/src/clearingHouse.js +0 -3433
  88. package/src/clearingHouseConfig.js +0 -2
  89. package/src/clearingHouseUser.js +0 -874
  90. package/src/clearingHouseUserConfig.js +0 -2
  91. package/src/clearingHouseUserStats.js +0 -115
  92. package/src/clearingHouseUserStatsConfig.js +0 -2
  93. package/src/config.js +0 -80
  94. package/src/constants/numericConstants.js +0 -48
  95. package/src/constants/perpMarkets.js +0 -42
  96. package/src/constants/spotMarkets.js +0 -51
  97. package/src/events/eventList.js +0 -120
  98. package/src/events/eventSubscriber.js +0 -202
  99. package/src/events/fetchLogs.js +0 -117
  100. package/src/events/pollingLogProvider.js +0 -113
  101. package/src/events/sort.js +0 -41
  102. package/src/events/txEventCache.js +0 -74
  103. package/src/events/types.js +0 -25
  104. package/src/events/webSocketLogProvider.js +0 -76
  105. package/src/factory/bigNum.js +0 -393
  106. package/src/factory/oracleClient.js +0 -20
  107. package/src/index.js +0 -75
  108. package/src/math/amm.js +0 -422
  109. package/src/math/auction.js +0 -42
  110. package/src/math/conversion.js +0 -12
  111. package/src/math/funding.js +0 -296
  112. package/src/math/insurance.js +0 -27
  113. package/src/math/margin.js +0 -77
  114. package/src/math/market.js +0 -105
  115. package/src/math/oracles.js +0 -56
  116. package/src/math/orders.js +0 -153
  117. package/src/math/position.js +0 -172
  118. package/src/math/repeg.js +0 -128
  119. package/src/math/spotBalance.js +0 -176
  120. package/src/math/spotMarket.js +0 -8
  121. package/src/math/spotPosition.js +0 -8
  122. package/src/math/trade.js +0 -260
  123. package/src/math/utils.js +0 -27
  124. package/src/oracles/oracleClientCache.js +0 -20
  125. package/src/oracles/pythClient.js +0 -81
  126. package/src/oracles/quoteAssetOracleClient.js +0 -63
  127. package/src/oracles/switchboardClient.js +0 -101
  128. package/src/oracles/types.js +0 -2
  129. package/src/orderParams.js +0 -28
  130. package/src/serum/serumSubscriber.js +0 -102
  131. package/src/serum/types.js +0 -2
  132. package/src/slot/SlotSubscriber.js +0 -86
  133. package/src/token/index.js +0 -38
  134. package/src/tokenFaucet.js +0 -323
  135. package/src/tx/retryTxSender.js +0 -280
  136. package/src/tx/types.js +0 -2
  137. package/src/tx/utils.js +0 -18
  138. package/src/types.js +0 -216
  139. package/src/userName.js +0 -20
  140. package/src/util/computeUnits.js +0 -62
  141. package/src/util/promiseTimeout.js +0 -14
  142. package/src/util/tps.js +0 -61
  143. package/src/wallet.js +0 -72
@@ -9,6 +9,7 @@ const spotBalance_1 = require("./math/spotBalance");
9
9
  const margin_1 = require("./math/margin");
10
10
  const pollingUserAccountSubscriber_1 = require("./accounts/pollingUserAccountSubscriber");
11
11
  const webSocketUserAccountSubscriber_1 = require("./accounts/webSocketUserAccountSubscriber");
12
+ const spotPosition_1 = require("./math/spotPosition");
12
13
  class ClearingHouseUser {
13
14
  constructor(config) {
14
15
  var _a;
@@ -72,7 +73,7 @@ class ClearingHouseUser {
72
73
  openOrders: numericConstants_1.ZERO,
73
74
  openBids: numericConstants_1.ZERO,
74
75
  openAsks: numericConstants_1.ZERO,
75
- realizedPnl: numericConstants_1.ZERO,
76
+ settledPnl: numericConstants_1.ZERO,
76
77
  lpShares: numericConstants_1.ZERO,
77
78
  lastFeePerLp: numericConstants_1.ZERO,
78
79
  lastNetBaseAssetAmountPerLp: numericConstants_1.ZERO,
@@ -212,62 +213,8 @@ class ClearingHouseUser {
212
213
  /**
213
214
  * @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION
214
215
  */
215
- getMarginRequirement(type, liquidationBuffer) {
216
- return this.getUserAccount()
217
- .perpPositions.reduce((marginRequirement, perpPosition) => {
218
- const market = this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex);
219
- if (perpPosition.lpShares.gt(numericConstants_1.ZERO)) {
220
- // is an lp
221
- // clone so we dont mutate the position
222
- perpPosition = this.getClonedPosition(perpPosition);
223
- // settle position
224
- const [settledPosition, dustBaa, _] = this.getSettledLPPosition(market.marketIndex);
225
- perpPosition.baseAssetAmount =
226
- settledPosition.baseAssetAmount.add(dustBaa);
227
- perpPosition.quoteAssetAmount = settledPosition.quoteAssetAmount;
228
- // open orders
229
- let openAsks;
230
- if (market.amm.maxBaseAssetReserve > market.amm.baseAssetReserve) {
231
- openAsks = market.amm.maxBaseAssetReserve
232
- .sub(market.amm.baseAssetReserve)
233
- .mul(perpPosition.lpShares)
234
- .div(market.amm.sqrtK)
235
- .mul(new _1.BN(-1));
236
- }
237
- else {
238
- openAsks = numericConstants_1.ZERO;
239
- }
240
- let openBids;
241
- if (market.amm.minBaseAssetReserve < market.amm.baseAssetReserve) {
242
- openBids = market.amm.baseAssetReserve
243
- .sub(market.amm.minBaseAssetReserve)
244
- .mul(perpPosition.lpShares)
245
- .div(market.amm.sqrtK);
246
- }
247
- else {
248
- openBids = numericConstants_1.ZERO;
249
- }
250
- perpPosition.openAsks = perpPosition.openAsks.add(openAsks);
251
- perpPosition.openBids = perpPosition.openBids.add(openBids);
252
- }
253
- let valuationPrice = this.getOracleDataForMarket(market.marketIndex).price;
254
- if ((0, types_1.isVariant)(market.status, 'settlement')) {
255
- valuationPrice = market.settlementPrice;
256
- }
257
- const worstCaseBaseAssetAmount = (0, margin_1.calculateWorstCaseBaseAssetAmount)(perpPosition);
258
- const worstCaseAssetValue = worstCaseBaseAssetAmount
259
- .abs()
260
- .mul(valuationPrice)
261
- .div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO.mul(numericConstants_1.MARK_PRICE_PRECISION));
262
- const positionMarginRequirement = worstCaseAssetValue
263
- .mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market, worstCaseBaseAssetAmount.abs(), type)))
264
- .div(numericConstants_1.MARGIN_PRECISION);
265
- if (liquidationBuffer !== undefined) {
266
- positionMarginRequirement.add(worstCaseAssetValue.mul(liquidationBuffer).div(numericConstants_1.MARGIN_PRECISION));
267
- }
268
- return marginRequirement.add(positionMarginRequirement);
269
- }, numericConstants_1.ZERO)
270
- .add(this.getSpotMarketLiabilityValue(undefined, type, liquidationBuffer));
216
+ getMarginRequirement(marginCategory, liquidationBuffer) {
217
+ return this.getTotalPerpPositionValue(marginCategory, liquidationBuffer, true).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, liquidationBuffer, true));
271
218
  }
272
219
  /**
273
220
  * @returns The initial margin requirement in USDC. : QUOTE_PRECISION
@@ -315,55 +262,127 @@ class ClearingHouseUser {
315
262
  return pnl.add((0, _1.calculatePositionFundingPNL)(market, perpPosition));
316
263
  }, numericConstants_1.ZERO);
317
264
  }
318
- getSpotMarketLiabilityValue(marketIndex, withWeightMarginCategory, liquidationBuffer) {
265
+ getSpotMarketLiabilityValue(marketIndex, marginCategory, liquidationBuffer, includeOpenOrders) {
319
266
  return this.getUserAccount().spotPositions.reduce((totalLiabilityValue, spotPosition) => {
320
- if (spotPosition.balance.eq(numericConstants_1.ZERO) ||
321
- (0, types_1.isVariant)(spotPosition.balanceType, 'deposit') ||
267
+ if ((0, spotPosition_1.isSpotPositionAvailable)(spotPosition) ||
322
268
  (marketIndex !== undefined &&
323
269
  !spotPosition.marketIndex.eq(marketIndex))) {
324
270
  return totalLiabilityValue;
325
271
  }
326
- // Todo this needs to account for whether it's based on initial or maintenance requirements
327
272
  const spotMarketAccount = this.clearingHouse.getSpotMarketAccount(spotPosition.marketIndex);
328
- const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.balance, spotMarketAccount, spotPosition.balanceType);
329
- let liabilityValue = tokenAmount
330
- .mul(this.getOracleDataForSpotMarket(spotMarketAccount.marketIndex).price)
331
- .div(numericConstants_1.MARK_PRICE_PRECISION)
332
- .div(new _1.BN(10).pow(new _1.BN(spotMarketAccount.decimals).sub(numericConstants_1.SPOT_MARKET_BALANCE_PRECISION_EXP)));
333
- if (withWeightMarginCategory !== undefined) {
334
- let weight = (0, spotBalance_1.calculateLiabilityWeight)(tokenAmount, spotMarketAccount, withWeightMarginCategory);
335
- if (liquidationBuffer !== undefined) {
336
- weight = weight.add(liquidationBuffer);
273
+ if (spotPosition.marketIndex.eq(numericConstants_1.QUOTE_SPOT_MARKET_INDEX)) {
274
+ if ((0, types_1.isVariant)(spotPosition.balanceType, 'borrow')) {
275
+ const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.balance, spotMarketAccount, spotPosition.balanceType);
276
+ let weight = numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION;
277
+ if (marginCategory === 'Initial') {
278
+ weight = _1.BN.max(weight, new _1.BN(this.getUserAccount().customMarginRatio));
279
+ }
280
+ const weightedTokenValue = tokenAmount
281
+ .mul(weight)
282
+ .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
283
+ return totalLiabilityValue.add(weightedTokenValue);
284
+ }
285
+ else {
286
+ return totalLiabilityValue;
287
+ }
288
+ }
289
+ const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
290
+ if (!includeOpenOrders) {
291
+ if ((0, types_1.isVariant)(spotPosition.balanceType, 'borrow')) {
292
+ const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.balance, spotMarketAccount, spotPosition.balanceType);
293
+ const liabilityValue = this.getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer);
294
+ return totalLiabilityValue.add(liabilityValue);
295
+ }
296
+ else {
297
+ return totalLiabilityValue;
337
298
  }
338
- liabilityValue = liabilityValue
299
+ }
300
+ const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, this.getOracleDataForSpotMarket(spotPosition.marketIndex));
301
+ let newTotalLiabilityValue = totalLiabilityValue;
302
+ if (worstCaseTokenAmount.lt(numericConstants_1.ZERO)) {
303
+ const baseLiabilityValue = this.getSpotLiabilityValue(worstCaseTokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer);
304
+ newTotalLiabilityValue =
305
+ newTotalLiabilityValue.add(baseLiabilityValue);
306
+ }
307
+ if (worstCaseQuoteTokenAmount.lt(numericConstants_1.ZERO)) {
308
+ let weight = numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION;
309
+ if (marginCategory === 'Initial') {
310
+ weight = _1.BN.max(weight, new _1.BN(this.getUserAccount().customMarginRatio));
311
+ }
312
+ const weightedTokenValue = worstCaseQuoteTokenAmount
339
313
  .mul(weight)
340
314
  .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
315
+ newTotalLiabilityValue =
316
+ newTotalLiabilityValue.add(weightedTokenValue);
341
317
  }
342
- return totalLiabilityValue.add(liabilityValue);
318
+ return newTotalLiabilityValue;
343
319
  }, numericConstants_1.ZERO);
344
320
  }
345
- getSpotMarketAssetValue(marketIndex, withWeightMarginCategory) {
321
+ getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer) {
322
+ let liabilityValue = (0, _1.getTokenValue)(tokenAmount, spotMarketAccount.decimals, oraclePriceData);
323
+ if (marginCategory !== undefined) {
324
+ let weight = (0, spotBalance_1.calculateLiabilityWeight)(tokenAmount, spotMarketAccount, marginCategory);
325
+ if (marginCategory === 'Initial') {
326
+ weight = _1.BN.max(weight, new _1.BN(this.getUserAccount().customMarginRatio));
327
+ }
328
+ if (liquidationBuffer !== undefined) {
329
+ weight = weight.add(liquidationBuffer);
330
+ }
331
+ liabilityValue = liabilityValue
332
+ .mul(weight)
333
+ .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
334
+ }
335
+ return liabilityValue;
336
+ }
337
+ getSpotMarketAssetValue(marketIndex, marginCategory, includeOpenOrders) {
346
338
  return this.getUserAccount().spotPositions.reduce((totalAssetValue, spotPosition) => {
347
- if (spotPosition.balance.eq(numericConstants_1.ZERO) ||
348
- (0, types_1.isVariant)(spotPosition.balanceType, 'borrow') ||
339
+ if ((0, spotPosition_1.isSpotPositionAvailable)(spotPosition) ||
349
340
  (marketIndex !== undefined &&
350
341
  !spotPosition.marketIndex.eq(marketIndex))) {
351
342
  return totalAssetValue;
352
343
  }
353
344
  // Todo this needs to account for whether it's based on initial or maintenance requirements
354
345
  const spotMarketAccount = this.clearingHouse.getSpotMarketAccount(spotPosition.marketIndex);
355
- const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.balance, spotMarketAccount, spotPosition.balanceType);
356
- let assetValue = tokenAmount
357
- .mul(this.getOracleDataForSpotMarket(spotMarketAccount.marketIndex).price)
358
- .div(numericConstants_1.MARK_PRICE_PRECISION)
359
- .div(new _1.BN(10).pow(new _1.BN(spotMarketAccount.decimals).sub(numericConstants_1.SPOT_MARKET_BALANCE_PRECISION_EXP)));
360
- if (withWeightMarginCategory !== undefined) {
361
- const weight = (0, spotBalance_1.calculateAssetWeight)(tokenAmount, spotMarketAccount, withWeightMarginCategory);
362
- assetValue = assetValue.mul(weight).div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
346
+ if (spotPosition.marketIndex.eq(numericConstants_1.QUOTE_SPOT_MARKET_INDEX)) {
347
+ if ((0, types_1.isVariant)(spotPosition.balanceType, 'deposit')) {
348
+ const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.balance, spotMarketAccount, spotPosition.balanceType);
349
+ return totalAssetValue.add(tokenAmount);
350
+ }
351
+ else {
352
+ return totalAssetValue;
353
+ }
363
354
  }
364
- return totalAssetValue.add(assetValue);
355
+ const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
356
+ if (!includeOpenOrders) {
357
+ if ((0, types_1.isVariant)(spotPosition.balanceType, 'deposit')) {
358
+ const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.balance, spotMarketAccount, spotPosition.balanceType);
359
+ const assetValue = this.getSpotAssetValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory);
360
+ return totalAssetValue.add(assetValue);
361
+ }
362
+ else {
363
+ return totalAssetValue;
364
+ }
365
+ }
366
+ const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, this.getOracleDataForSpotMarket(spotPosition.marketIndex));
367
+ let newTotalAssetValue = totalAssetValue;
368
+ if (worstCaseTokenAmount.gt(numericConstants_1.ZERO)) {
369
+ const baseAssetValue = this.getSpotAssetValue(worstCaseTokenAmount, oraclePriceData, spotMarketAccount, marginCategory);
370
+ newTotalAssetValue = newTotalAssetValue.add(baseAssetValue);
371
+ }
372
+ if (worstCaseQuoteTokenAmount.gt(numericConstants_1.ZERO)) {
373
+ newTotalAssetValue = newTotalAssetValue.add(worstCaseQuoteTokenAmount);
374
+ }
375
+ return newTotalAssetValue;
365
376
  }, numericConstants_1.ZERO);
366
377
  }
378
+ getSpotAssetValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory) {
379
+ let assetValue = (0, _1.getTokenValue)(tokenAmount, spotMarketAccount.decimals, oraclePriceData);
380
+ if (marginCategory !== undefined) {
381
+ const weight = (0, spotBalance_1.calculateAssetWeight)(tokenAmount, spotMarketAccount, marginCategory);
382
+ assetValue = assetValue.mul(weight).div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
383
+ }
384
+ return assetValue;
385
+ }
367
386
  getNetSpotMarketValue(withWeightMarginCategory) {
368
387
  return this.getSpotMarketAssetValue(undefined, withWeightMarginCategory).sub(this.getSpotMarketLiabilityValue(undefined, withWeightMarginCategory));
369
388
  }
@@ -372,24 +391,80 @@ class ClearingHouseUser {
372
391
  * @returns : Precision QUOTE_PRECISION
373
392
  */
374
393
  getTotalCollateral(marginCategory = 'Initial') {
375
- return this.getSpotMarketAssetValue(undefined, marginCategory).add(this.getUnrealizedPNL(true, undefined, marginCategory));
394
+ return this.getSpotMarketAssetValue(undefined, marginCategory, true).add(this.getUnrealizedPNL(true, undefined, marginCategory));
376
395
  }
377
396
  /**
378
397
  * calculates sum of position value across all positions in margin system
379
398
  * @returns : Precision QUOTE_PRECISION
380
399
  */
381
- getTotalPositionValue() {
382
- return this.getUserAccount().perpPositions.reduce((positionValue, perpPosition) => {
400
+ getTotalPerpPositionValue(marginCategory, liquidationBuffer, includeOpenOrders) {
401
+ return this.getUserAccount().perpPositions.reduce((totalPerpValue, perpPosition) => {
383
402
  const market = this.clearingHouse.getPerpMarketAccount(perpPosition.marketIndex);
384
- const posVal = (0, margin_1.calculateBaseAssetValueWithOracle)(market, perpPosition, this.getOracleDataForMarket(market.marketIndex));
385
- return positionValue.add(posVal);
403
+ if (perpPosition.lpShares.gt(numericConstants_1.ZERO)) {
404
+ // is an lp
405
+ // clone so we dont mutate the position
406
+ perpPosition = this.getClonedPosition(perpPosition);
407
+ // settle position
408
+ const [settledPosition, dustBaa, _] = this.getSettledLPPosition(market.marketIndex);
409
+ perpPosition.baseAssetAmount =
410
+ settledPosition.baseAssetAmount.add(dustBaa);
411
+ perpPosition.quoteAssetAmount = settledPosition.quoteAssetAmount;
412
+ // open orders
413
+ let openAsks;
414
+ if (market.amm.maxBaseAssetReserve > market.amm.baseAssetReserve) {
415
+ openAsks = market.amm.maxBaseAssetReserve
416
+ .sub(market.amm.baseAssetReserve)
417
+ .mul(perpPosition.lpShares)
418
+ .div(market.amm.sqrtK)
419
+ .mul(new _1.BN(-1));
420
+ }
421
+ else {
422
+ openAsks = numericConstants_1.ZERO;
423
+ }
424
+ let openBids;
425
+ if (market.amm.minBaseAssetReserve < market.amm.baseAssetReserve) {
426
+ openBids = market.amm.baseAssetReserve
427
+ .sub(market.amm.minBaseAssetReserve)
428
+ .mul(perpPosition.lpShares)
429
+ .div(market.amm.sqrtK);
430
+ }
431
+ else {
432
+ openBids = numericConstants_1.ZERO;
433
+ }
434
+ perpPosition.openAsks = perpPosition.openAsks.add(openAsks);
435
+ perpPosition.openBids = perpPosition.openBids.add(openBids);
436
+ }
437
+ let valuationPrice = this.getOracleDataForMarket(market.marketIndex).price;
438
+ if ((0, types_1.isVariant)(market.status, 'settlement')) {
439
+ valuationPrice = market.settlementPrice;
440
+ }
441
+ const baseAssetAmount = includeOpenOrders
442
+ ? (0, margin_1.calculateWorstCaseBaseAssetAmount)(perpPosition)
443
+ : perpPosition.baseAssetAmount;
444
+ let baseAssetValue = baseAssetAmount
445
+ .abs()
446
+ .mul(valuationPrice)
447
+ .div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO.mul(numericConstants_1.MARK_PRICE_PRECISION));
448
+ if (marginCategory) {
449
+ let marginRatio = new _1.BN((0, _1.calculateMarketMarginRatio)(market, baseAssetAmount.abs(), marginCategory));
450
+ if (marginCategory === 'Initial') {
451
+ marginRatio = _1.BN.max(marginRatio, new _1.BN(this.getUserAccount().customMarginRatio));
452
+ }
453
+ if (liquidationBuffer !== undefined) {
454
+ marginRatio = marginRatio.add(liquidationBuffer);
455
+ }
456
+ baseAssetValue = baseAssetValue
457
+ .mul(marginRatio)
458
+ .div(numericConstants_1.MARGIN_PRECISION);
459
+ }
460
+ return totalPerpValue.add(baseAssetValue);
386
461
  }, numericConstants_1.ZERO);
387
462
  }
388
463
  /**
389
464
  * calculates position value in margin system
390
465
  * @returns : Precision QUOTE_PRECISION
391
466
  */
392
- getPositionValue(marketIndex, oraclePriceData) {
467
+ getPerpPositionValue(marketIndex, oraclePriceData) {
393
468
  const userPosition = this.getUserPosition(marketIndex) || this.getEmptyPosition(marketIndex);
394
469
  const market = this.clearingHouse.getPerpMarketAccount(userPosition.marketIndex);
395
470
  return (0, margin_1.calculateBaseAssetValueWithOracle)(market, userPosition, oraclePriceData);
@@ -449,13 +524,13 @@ class ClearingHouseUser {
449
524
  * calculates current user leverage across all positions
450
525
  * @returns : Precision TEN_THOUSAND
451
526
  */
452
- getLeverage() {
453
- const totalCollateral = this.getTotalCollateral();
454
- const totalPositionValue = this.getTotalPositionValue();
455
- if (totalPositionValue.eq(numericConstants_1.ZERO) && totalCollateral.eq(numericConstants_1.ZERO)) {
527
+ getLeverage(marginCategory) {
528
+ const totalLiabilityValue = this.getTotalPerpPositionValue(marginCategory, undefined, true).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, undefined, true));
529
+ const totalAssetValue = this.getSpotMarketAssetValue(undefined, marginCategory, true).add(this.getUnrealizedPNL(true, undefined, marginCategory));
530
+ if (totalAssetValue.eq(numericConstants_1.ZERO) && totalLiabilityValue.eq(numericConstants_1.ZERO)) {
456
531
  return numericConstants_1.ZERO;
457
532
  }
458
- return totalPositionValue.mul(numericConstants_1.TEN_THOUSAND).div(totalCollateral);
533
+ return totalLiabilityValue.mul(numericConstants_1.TEN_THOUSAND).div(totalAssetValue);
459
534
  }
460
535
  /**
461
536
  * calculates max allowable leverage exceeding hitting requirement category
@@ -475,12 +550,13 @@ class ClearingHouseUser {
475
550
  * calculates margin ratio: total collateral / |total position value|
476
551
  * @returns : Precision TEN_THOUSAND
477
552
  */
478
- getMarginRatio() {
479
- const totalPositionValue = this.getTotalPositionValue();
480
- if (totalPositionValue.eq(numericConstants_1.ZERO)) {
553
+ getMarginRatio(marginCategory) {
554
+ const totalLiabilityValue = this.getTotalPerpPositionValue(marginCategory, undefined, true).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, undefined, true));
555
+ if (totalLiabilityValue.eq(numericConstants_1.ZERO)) {
481
556
  return numericConstants_1.BN_MAX;
482
557
  }
483
- return this.getTotalCollateral().mul(numericConstants_1.TEN_THOUSAND).div(totalPositionValue);
558
+ const totalAssetValue = this.getSpotMarketAssetValue(undefined, marginCategory, true).add(this.getUnrealizedPNL(true, undefined, marginCategory));
559
+ return totalAssetValue.mul(numericConstants_1.TEN_THOUSAND).div(totalLiabilityValue);
484
560
  }
485
561
  canBeLiquidated() {
486
562
  const totalCollateral = this.getTotalCollateral();
@@ -530,7 +606,7 @@ class ClearingHouseUser {
530
606
  3. (10k - 4k) / (100k - 4k) = 6k/96k => .0625 */
531
607
  const totalCollateral = this.getTotalCollateral();
532
608
  // calculate the total position value ignoring any value from the target market of the trade
533
- const totalPositionValueExcludingTargetMarket = this.getTotalPositionValueExcludingMarket(perpPosition.marketIndex);
609
+ const totalPositionValueExcludingTargetMarket = this.getTotalPerpPositionValueExcludingMarket(perpPosition.marketIndex);
534
610
  const currentPerpPosition = this.getUserPosition(perpPosition.marketIndex) ||
535
611
  this.getEmptyPosition(perpPosition.marketIndex);
536
612
  const currentPerpPositionBaseSize = currentPerpPosition.baseAssetAmount;
@@ -546,7 +622,7 @@ class ClearingHouseUser {
546
622
  openOrders: new _1.BN(0),
547
623
  openBids: new _1.BN(0),
548
624
  openAsks: new _1.BN(0),
549
- realizedPnl: numericConstants_1.ZERO,
625
+ settledPnl: numericConstants_1.ZERO,
550
626
  lpShares: numericConstants_1.ZERO,
551
627
  lastFeePerLp: numericConstants_1.ZERO,
552
628
  lastNetBaseAssetAmountPerLp: numericConstants_1.ZERO,
@@ -667,7 +743,7 @@ class ClearingHouseUser {
667
743
  // 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.
668
744
  const oppositeSizeValueUSDC = targetingSameSide
669
745
  ? numericConstants_1.ZERO
670
- : this.getPositionValue(targetMarketIndex, oracleData);
746
+ : this.getPerpPositionValue(targetMarketIndex, oracleData);
671
747
  let maxPositionSize = this.getBuyingPower(targetMarketIndex);
672
748
  if (maxPositionSize.gte(numericConstants_1.ZERO)) {
673
749
  if (oppositeSizeValueUSDC.eq(numericConstants_1.ZERO)) {
@@ -683,7 +759,7 @@ class ClearingHouseUser {
683
759
  // current leverage is greater than max leverage - can only reduce position size
684
760
  if (!targetingSameSide) {
685
761
  const market = this.clearingHouse.getPerpMarketAccount(targetMarketIndex);
686
- const perpPositionValue = this.getPositionValue(targetMarketIndex, oracleData);
762
+ const perpPositionValue = this.getPerpPositionValue(targetMarketIndex, oracleData);
687
763
  const totalCollateral = this.getTotalCollateral();
688
764
  const marginRequirement = this.getInitialMarginRequirement();
689
765
  const marginFreedByClosing = perpPositionValue
@@ -722,7 +798,7 @@ class ClearingHouseUser {
722
798
  const currentPosition = this.getUserPosition(targetMarketIndex) ||
723
799
  this.getEmptyPosition(targetMarketIndex);
724
800
  const oracleData = this.getOracleDataForMarket(targetMarketIndex);
725
- let currentPositionQuoteAmount = this.getPositionValue(targetMarketIndex, oracleData);
801
+ let currentPositionQuoteAmount = this.getPerpPositionValue(targetMarketIndex, oracleData);
726
802
  const currentSide = currentPosition && currentPosition.baseAssetAmount.isNeg()
727
803
  ? _1.PositionDirection.SHORT
728
804
  : _1.PositionDirection.LONG;
@@ -733,7 +809,7 @@ class ClearingHouseUser {
733
809
  const currentPerpPositionAfterTrade = currentPositionQuoteAmount
734
810
  .add(tradeQuoteAmount)
735
811
  .abs();
736
- const totalPositionAfterTradeExcludingTargetMarket = this.getTotalPositionValueExcludingMarket(targetMarketIndex);
812
+ const totalPositionAfterTradeExcludingTargetMarket = this.getTotalPerpPositionValueExcludingMarket(targetMarketIndex);
737
813
  const totalCollateral = this.getTotalCollateral();
738
814
  if (totalCollateral.gt(numericConstants_1.ZERO)) {
739
815
  const newLeverage = currentPerpPositionAfterTrade
@@ -763,15 +839,15 @@ class ClearingHouseUser {
763
839
  * @param marketToIgnore
764
840
  * @returns positionValue : Precision QUOTE_PRECISION
765
841
  */
766
- getTotalPositionValueExcludingMarket(marketToIgnore) {
842
+ getTotalPerpPositionValueExcludingMarket(marketToIgnore) {
767
843
  const currentPerpPosition = this.getUserPosition(marketToIgnore) ||
768
844
  this.getEmptyPosition(marketToIgnore);
769
845
  const oracleData = this.getOracleDataForMarket(marketToIgnore);
770
846
  let currentPerpPositionValueUSDC = numericConstants_1.ZERO;
771
847
  if (currentPerpPosition) {
772
- currentPerpPositionValueUSDC = this.getPositionValue(marketToIgnore, oracleData);
848
+ currentPerpPositionValueUSDC = this.getPerpPositionValue(marketToIgnore, oracleData);
773
849
  }
774
- return this.getTotalPositionValue().sub(currentPerpPositionValueUSDC);
850
+ return this.getTotalPerpPositionValue().sub(currentPerpPositionValueUSDC);
775
851
  }
776
852
  getOracleDataForMarket(marketIndex) {
777
853
  const oracleKey = this.clearingHouse.getPerpMarketAccount(marketIndex).amm.oracle;
package/lib/config.js CHANGED
@@ -7,7 +7,7 @@ exports.configs = {
7
7
  devnet: {
8
8
  ENV: 'devnet',
9
9
  PYTH_ORACLE_MAPPING_ADDRESS: 'BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2',
10
- CLEARING_HOUSE_PROGRAM_ID: '3v1iEjbSSLSSYyt1pmx4UB5rqJGurmz71RibXF7X6UF3',
10
+ CLEARING_HOUSE_PROGRAM_ID: 'CUowJP5Dea7pkc2mKrkX2RuZUrPXL3u419iBRXyZEWQz',
11
11
  USDC_MINT_ADDRESS: '8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2',
12
12
  PERP_MARKETS: perpMarkets_1.DevnetPerpMarkets,
13
13
  SPOT_MARKETS: spotMarkets_1.DevnetSpotMarkets,
@@ -0,0 +1,73 @@
1
+ /// <reference types="bn.js" />
2
+ import { NodeList } from './NodeList';
3
+ import { MarketType, BN, ClearingHouse, Order, SpotMarketAccount, PerpMarketAccount, OraclePriceData, SlotSubscriber, UserMap, MarketTypeStr } from '..';
4
+ import { PublicKey } from '@solana/web3.js';
5
+ import { DLOBNode, TriggerOrderNode } from '..';
6
+ export declare type MarketNodeLists = {
7
+ limit: {
8
+ ask: NodeList<'limit'>;
9
+ bid: NodeList<'limit'>;
10
+ };
11
+ floatingLimit: {
12
+ ask: NodeList<'floatingLimit'>;
13
+ bid: NodeList<'floatingLimit'>;
14
+ };
15
+ market: {
16
+ ask: NodeList<'market'>;
17
+ bid: NodeList<'market'>;
18
+ };
19
+ trigger: {
20
+ above: NodeList<'trigger'>;
21
+ below: NodeList<'trigger'>;
22
+ };
23
+ };
24
+ declare type OrderBookCallback = () => void;
25
+ export declare type NodeToFill = {
26
+ node: DLOBNode;
27
+ makerNode?: DLOBNode;
28
+ };
29
+ export declare type NodeToTrigger = {
30
+ node: TriggerOrderNode;
31
+ };
32
+ declare type Side = 'ask' | 'bid' | 'both';
33
+ export declare class DLOB {
34
+ openOrders: Map<MarketTypeStr, Set<string>>;
35
+ orderLists: Map<MarketTypeStr, Map<number, MarketNodeLists>>;
36
+ marketIndexToAccount: Map<MarketTypeStr, Map<number, PerpMarketAccount | SpotMarketAccount>>;
37
+ silent: boolean;
38
+ initialized: boolean;
39
+ /**
40
+ *
41
+ * @param perpMarkets The perp markets to maintain a DLOB for
42
+ * @param spotMarkets The spot markets to maintain a DLOB for
43
+ * @param silent set to true to prevent logging on inserts and removals
44
+ */
45
+ constructor(perpMarkets: PerpMarketAccount[], spotMarkets: SpotMarketAccount[], silent?: boolean);
46
+ /**
47
+ * initializes a new DLOB instance
48
+ *
49
+ * @param clearingHouse The ClearingHouse instance to use for price data
50
+ * @returns a promise that resolves when the DLOB is initialized
51
+ */
52
+ init(clearingHouse: ClearingHouse, userMap?: UserMap): Promise<boolean>;
53
+ insertOrder(order: Order, userAccount: PublicKey, onInsert?: OrderBookCallback): void;
54
+ trigger(order: Order, userAccount: PublicKey, onTrigger?: OrderBookCallback): void;
55
+ getListForOrder(order: Order): NodeList<any>;
56
+ findNodesToFill(marketIndex: BN, vBid: BN | undefined, vAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): NodeToFill[];
57
+ findCrossingNodesToFill(marketIndex: BN, vBid: BN | undefined, vAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): NodeToFill[];
58
+ findMarketNodesToFill(marketIndex: BN, slot: number, marketType: MarketType): NodeToFill[];
59
+ findJitAuctionNodesToFill(marketIndex: BN, slot: number, marketType: MarketType): NodeToFill[];
60
+ getMarketBids(marketIndex: BN, marketType: MarketType): Generator<DLOBNode>;
61
+ getMarketAsks(marketIndex: BN, marketType: MarketType): Generator<DLOBNode>;
62
+ getAsks(marketIndex: BN, vAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
63
+ getBids(marketIndex: BN, vBid: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): Generator<DLOBNode>;
64
+ findCrossingOrders(askNode: DLOBNode, bidNode: DLOBNode, oraclePriceData: OraclePriceData, slot: number): {
65
+ crossingNodes?: NodeToFill;
66
+ exhaustedSide?: Side;
67
+ };
68
+ getBestAsk(marketIndex: BN, vAsk: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): BN;
69
+ getBestBid(marketIndex: BN, vBid: BN | undefined, slot: number, marketType: MarketType, oraclePriceData: OraclePriceData): BN;
70
+ findNodesToTrigger(marketIndex: BN, slot: number, oraclePrice: BN, marketType: MarketType): NodeToTrigger[];
71
+ printTopOfOrderLists(sdkConfig: any, clearingHouse: ClearingHouse, slotSubscriber: SlotSubscriber, marketIndex: BN, marketType: MarketType): void;
72
+ }
73
+ export {};