@drift-labs/sdk 2.37.1-beta.6 → 2.37.1-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.37.1-beta.6
1
+ 2.37.1-beta.7
package/lib/types.d.ts CHANGED
@@ -1117,3 +1117,16 @@ export type PerpMarketExtendedInfo = {
1117
1117
  pnlPoolValue: BN;
1118
1118
  contractTier: ContractTier;
1119
1119
  };
1120
+ export type HealthComponents = {
1121
+ deposits: HealthComponent[];
1122
+ borrows: HealthComponent[];
1123
+ perpPositions: HealthComponent[];
1124
+ perpPnl: HealthComponent[];
1125
+ };
1126
+ export type HealthComponent = {
1127
+ marketIndex: number;
1128
+ size: BN;
1129
+ value: BN;
1130
+ weight: BN;
1131
+ weightedValue: BN;
1132
+ };
package/lib/user.d.ts CHANGED
@@ -3,7 +3,7 @@ import { PublicKey } from '@solana/web3.js';
3
3
  import { EventEmitter } from 'events';
4
4
  import StrictEventEmitter from 'strict-event-emitter-types';
5
5
  import { DriftClient } from './driftClient';
6
- import { MarginCategory, Order, UserAccount, PerpPosition, SpotPosition, PerpMarketAccount } from './types';
6
+ import { MarginCategory, Order, UserAccount, PerpPosition, SpotPosition, PerpMarketAccount, HealthComponents } from './types';
7
7
  import { UserAccountSubscriber, UserAccountEvents, DataAndSlot } from './accounts/types';
8
8
  import { PositionDirection, BN, SpotMarketAccount, MarketType } from '.';
9
9
  import { OraclePriceData } from './oracles/types';
@@ -358,6 +358,9 @@ export declare class User {
358
358
  perpTier: number;
359
359
  spotTier: number;
360
360
  };
361
+ getHealthComponents({ marginCategory, }: {
362
+ marginCategory: MarginCategory;
363
+ }): HealthComponents;
361
364
  /**
362
365
  * Get the total position value, excluding any position coming from the given target market
363
366
  * @param marketToIgnore
package/lib/user.js CHANGED
@@ -464,7 +464,7 @@ class User {
464
464
  }
465
465
  positionUnrealizedPnl = positionUnrealizedPnl
466
466
  .mul(quotePrice)
467
- .div(new _1.BN(numericConstants_1.PRICE_PRECISION));
467
+ .div(numericConstants_1.PRICE_PRECISION);
468
468
  if (withWeightMarginCategory !== undefined) {
469
469
  if (positionUnrealizedPnl.gt(numericConstants_1.ZERO)) {
470
470
  positionUnrealizedPnl = positionUnrealizedPnl
@@ -676,7 +676,7 @@ class User {
676
676
  let baseAssetValue = baseAssetAmount
677
677
  .abs()
678
678
  .mul(valuationPrice)
679
- .div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO.mul(numericConstants_1.PRICE_PRECISION));
679
+ .div(numericConstants_1.BASE_PRECISION);
680
680
  if (marginCategory) {
681
681
  let marginRatio = new _1.BN((0, _1.calculateMarketMarginRatio)(market, baseAssetAmount.abs(), marginCategory));
682
682
  if (marginCategory === 'Initial') {
@@ -1722,6 +1722,146 @@ class User {
1722
1722
  spotTier: safestSpotTier,
1723
1723
  };
1724
1724
  }
1725
+ getHealthComponents({ marginCategory, }) {
1726
+ const healthComponents = {
1727
+ deposits: [],
1728
+ borrows: [],
1729
+ perpPositions: [],
1730
+ perpPnl: [],
1731
+ };
1732
+ for (const perpPosition of this.getActivePerpPositions()) {
1733
+ const perpMarket = this.driftClient.getPerpMarketAccount(perpPosition.marketIndex);
1734
+ const oraclePriceData = this.driftClient.getOraclePriceDataAndSlot(perpMarket.amm.oracle).data;
1735
+ const oraclePrice = oraclePriceData.price;
1736
+ const worstCaseBaseAmount = (0, margin_1.calculateWorstCaseBaseAssetAmount)(perpPosition);
1737
+ const marginRatio = new _1.BN((0, _1.calculateMarketMarginRatio)(perpMarket, worstCaseBaseAmount.abs(), marginCategory));
1738
+ const quoteSpotMarket = this.driftClient.getSpotMarketAccount(perpMarket.quoteSpotMarketIndex);
1739
+ const quoteOraclePriceData = this.driftClient.getOraclePriceDataAndSlot(quoteSpotMarket.oracle).data;
1740
+ const baseAssetValue = worstCaseBaseAmount
1741
+ .abs()
1742
+ .mul(oraclePrice)
1743
+ .div(numericConstants_1.BASE_PRECISION);
1744
+ let marginRequirement = baseAssetValue
1745
+ .mul(quoteOraclePriceData.price)
1746
+ .div(numericConstants_1.PRICE_PRECISION)
1747
+ .mul(marginRatio)
1748
+ .div(numericConstants_1.MARGIN_PRECISION);
1749
+ marginRequirement = marginRequirement.add(new _1.BN(perpPosition.openOrders).mul(numericConstants_1.OPEN_ORDER_MARGIN_REQUIREMENT));
1750
+ if (perpPosition.lpShares.gt(numericConstants_1.ZERO)) {
1751
+ marginRequirement = marginRequirement.add(_1.BN.max(numericConstants_1.QUOTE_PRECISION, oraclePrice
1752
+ .mul(perpMarket.amm.orderStepSize)
1753
+ .mul(numericConstants_1.QUOTE_PRECISION)
1754
+ .div(numericConstants_1.AMM_RESERVE_PRECISION)
1755
+ .div(numericConstants_1.PRICE_PRECISION)));
1756
+ }
1757
+ healthComponents.perpPositions.push({
1758
+ marketIndex: perpMarket.marketIndex,
1759
+ size: worstCaseBaseAmount,
1760
+ value: baseAssetValue,
1761
+ weight: marginRatio,
1762
+ weightedValue: marginRequirement,
1763
+ });
1764
+ const settledPerpPosition = this.getPerpPositionWithLPSettle(perpPosition.marketIndex, perpPosition)[0];
1765
+ const positionUnrealizedPnl = (0, _1.calculatePositionPNL)(perpMarket, settledPerpPosition, true, oraclePriceData);
1766
+ let pnlWeight;
1767
+ if (positionUnrealizedPnl.gt(numericConstants_1.ZERO)) {
1768
+ pnlWeight = (0, _1.calculateUnrealizedAssetWeight)(perpMarket, quoteSpotMarket, positionUnrealizedPnl, marginCategory, oraclePriceData);
1769
+ }
1770
+ else {
1771
+ pnlWeight = numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION;
1772
+ }
1773
+ const pnlValue = positionUnrealizedPnl
1774
+ .mul(quoteOraclePriceData.price)
1775
+ .div(numericConstants_1.PRICE_PRECISION);
1776
+ const wegithedPnlValue = pnlValue
1777
+ .mul(pnlWeight)
1778
+ .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
1779
+ healthComponents.perpPnl.push({
1780
+ marketIndex: perpMarket.marketIndex,
1781
+ size: positionUnrealizedPnl,
1782
+ value: wegithedPnlValue,
1783
+ weight: pnlWeight,
1784
+ weightedValue: wegithedPnlValue,
1785
+ });
1786
+ }
1787
+ let netQuoteValue = numericConstants_1.ZERO;
1788
+ for (const spotPosition of this.getActiveSpotPositions()) {
1789
+ const spotMarketAccount = this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
1790
+ const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
1791
+ if (spotPosition.marketIndex === numericConstants_1.QUOTE_SPOT_MARKET_INDEX) {
1792
+ const tokenAmount = (0, _1.getSignedTokenAmount)((0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType), spotPosition.balanceType);
1793
+ netQuoteValue = netQuoteValue.add(tokenAmount);
1794
+ continue;
1795
+ }
1796
+ const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] = (0, spotPosition_1.getWorstCaseTokenAmounts)(spotPosition, spotMarketAccount, oraclePriceData);
1797
+ netQuoteValue = netQuoteValue.add(worstCaseQuoteTokenAmount);
1798
+ const baseAssetValue = (0, _1.getTokenValue)(worstCaseTokenAmount.abs(), spotMarketAccount.decimals, oraclePriceData);
1799
+ const isLiability = (0, types_1.isVariant)(spotPosition.balanceType, 'borrow');
1800
+ let weight;
1801
+ if (isLiability) {
1802
+ weight = (0, spotBalance_1.calculateLiabilityWeight)(worstCaseTokenAmount.abs(), spotMarketAccount, marginCategory);
1803
+ }
1804
+ else {
1805
+ weight = (0, spotBalance_1.calculateAssetWeight)(worstCaseTokenAmount, spotMarketAccount, marginCategory);
1806
+ }
1807
+ const weightedValue = baseAssetValue
1808
+ .mul(weight)
1809
+ .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
1810
+ if (isLiability) {
1811
+ healthComponents.borrows.push({
1812
+ marketIndex: spotMarketAccount.marketIndex,
1813
+ size: worstCaseTokenAmount,
1814
+ value: baseAssetValue,
1815
+ weight: weight,
1816
+ weightedValue: weightedValue,
1817
+ });
1818
+ }
1819
+ else {
1820
+ healthComponents.deposits.push({
1821
+ marketIndex: spotMarketAccount.marketIndex,
1822
+ size: worstCaseTokenAmount,
1823
+ value: weightedValue,
1824
+ weight: weight,
1825
+ weightedValue: weightedValue,
1826
+ });
1827
+ }
1828
+ }
1829
+ if (!netQuoteValue.eq(numericConstants_1.ZERO)) {
1830
+ const spotMarketAccount = this.driftClient.getQuoteSpotMarketAccount();
1831
+ const oraclePriceData = this.getOracleDataForSpotMarket(numericConstants_1.QUOTE_SPOT_MARKET_INDEX);
1832
+ const baseAssetValue = (0, _1.getTokenValue)(netQuoteValue.abs(), spotMarketAccount.decimals, oraclePriceData);
1833
+ const isLiability = netQuoteValue.lt(numericConstants_1.ZERO);
1834
+ let weight;
1835
+ if (isLiability) {
1836
+ weight = (0, spotBalance_1.calculateLiabilityWeight)(netQuoteValue.abs(), spotMarketAccount, marginCategory);
1837
+ }
1838
+ else {
1839
+ weight = (0, spotBalance_1.calculateAssetWeight)(netQuoteValue, spotMarketAccount, marginCategory);
1840
+ }
1841
+ const weightedValue = baseAssetValue
1842
+ .mul(weight)
1843
+ .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
1844
+ if (isLiability) {
1845
+ healthComponents.borrows.push({
1846
+ marketIndex: spotMarketAccount.marketIndex,
1847
+ size: netQuoteValue,
1848
+ value: baseAssetValue,
1849
+ weight: weight,
1850
+ weightedValue: weightedValue,
1851
+ });
1852
+ }
1853
+ else {
1854
+ healthComponents.deposits.push({
1855
+ marketIndex: spotMarketAccount.marketIndex,
1856
+ size: netQuoteValue,
1857
+ value: weightedValue,
1858
+ weight: weight,
1859
+ weightedValue: weightedValue,
1860
+ });
1861
+ }
1862
+ }
1863
+ return healthComponents;
1864
+ }
1725
1865
  /**
1726
1866
  * Get the total position value, excluding any position coming from the given target market
1727
1867
  * @param marketToIgnore
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.37.1-beta.6",
3
+ "version": "2.37.1-beta.7",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
package/src/types.ts CHANGED
@@ -1100,3 +1100,18 @@ export type PerpMarketExtendedInfo = {
1100
1100
  pnlPoolValue: BN;
1101
1101
  contractTier: ContractTier;
1102
1102
  };
1103
+
1104
+ export type HealthComponents = {
1105
+ deposits: HealthComponent[];
1106
+ borrows: HealthComponent[];
1107
+ perpPositions: HealthComponent[];
1108
+ perpPnl: HealthComponent[];
1109
+ };
1110
+
1111
+ export type HealthComponent = {
1112
+ marketIndex: number;
1113
+ size: BN;
1114
+ value: BN;
1115
+ weight: BN;
1116
+ weightedValue: BN;
1117
+ };
package/src/user.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  SpotPosition,
12
12
  isOneOfVariant,
13
13
  PerpMarketAccount,
14
+ HealthComponents,
14
15
  } from './types';
15
16
  import { calculateEntryPrice, positionIsAvailable } from './math/position';
16
17
  import {
@@ -749,7 +750,7 @@ export class User {
749
750
 
750
751
  positionUnrealizedPnl = positionUnrealizedPnl
751
752
  .mul(quotePrice)
752
- .div(new BN(PRICE_PRECISION));
753
+ .div(PRICE_PRECISION);
753
754
 
754
755
  if (withWeightMarginCategory !== undefined) {
755
756
  if (positionUnrealizedPnl.gt(ZERO)) {
@@ -1231,7 +1232,7 @@ export class User {
1231
1232
  let baseAssetValue = baseAssetAmount
1232
1233
  .abs()
1233
1234
  .mul(valuationPrice)
1234
- .div(AMM_TO_QUOTE_PRECISION_RATIO.mul(PRICE_PRECISION));
1235
+ .div(BASE_PRECISION);
1235
1236
 
1236
1237
  if (marginCategory) {
1237
1238
  let marginRatio = new BN(
@@ -3117,6 +3118,253 @@ export class User {
3117
3118
  };
3118
3119
  }
3119
3120
 
3121
+ public getHealthComponents({
3122
+ marginCategory,
3123
+ }: {
3124
+ marginCategory: MarginCategory;
3125
+ }): HealthComponents {
3126
+ const healthComponents: HealthComponents = {
3127
+ deposits: [],
3128
+ borrows: [],
3129
+ perpPositions: [],
3130
+ perpPnl: [],
3131
+ };
3132
+
3133
+ for (const perpPosition of this.getActivePerpPositions()) {
3134
+ const perpMarket = this.driftClient.getPerpMarketAccount(
3135
+ perpPosition.marketIndex
3136
+ );
3137
+ const oraclePriceData = this.driftClient.getOraclePriceDataAndSlot(
3138
+ perpMarket.amm.oracle
3139
+ ).data;
3140
+ const oraclePrice = oraclePriceData.price;
3141
+ const worstCaseBaseAmount =
3142
+ calculateWorstCaseBaseAssetAmount(perpPosition);
3143
+
3144
+ const marginRatio = new BN(
3145
+ calculateMarketMarginRatio(
3146
+ perpMarket,
3147
+ worstCaseBaseAmount.abs(),
3148
+ marginCategory
3149
+ )
3150
+ );
3151
+
3152
+ const quoteSpotMarket = this.driftClient.getSpotMarketAccount(
3153
+ perpMarket.quoteSpotMarketIndex
3154
+ );
3155
+ const quoteOraclePriceData = this.driftClient.getOraclePriceDataAndSlot(
3156
+ quoteSpotMarket.oracle
3157
+ ).data;
3158
+
3159
+ const baseAssetValue = worstCaseBaseAmount
3160
+ .abs()
3161
+ .mul(oraclePrice)
3162
+ .div(BASE_PRECISION);
3163
+
3164
+ let marginRequirement = baseAssetValue
3165
+ .mul(quoteOraclePriceData.price)
3166
+ .div(PRICE_PRECISION)
3167
+ .mul(marginRatio)
3168
+ .div(MARGIN_PRECISION);
3169
+
3170
+ marginRequirement = marginRequirement.add(
3171
+ new BN(perpPosition.openOrders).mul(OPEN_ORDER_MARGIN_REQUIREMENT)
3172
+ );
3173
+
3174
+ if (perpPosition.lpShares.gt(ZERO)) {
3175
+ marginRequirement = marginRequirement.add(
3176
+ BN.max(
3177
+ QUOTE_PRECISION,
3178
+ oraclePrice
3179
+ .mul(perpMarket.amm.orderStepSize)
3180
+ .mul(QUOTE_PRECISION)
3181
+ .div(AMM_RESERVE_PRECISION)
3182
+ .div(PRICE_PRECISION)
3183
+ )
3184
+ );
3185
+ }
3186
+
3187
+ healthComponents.perpPositions.push({
3188
+ marketIndex: perpMarket.marketIndex,
3189
+ size: worstCaseBaseAmount,
3190
+ value: baseAssetValue,
3191
+ weight: marginRatio,
3192
+ weightedValue: marginRequirement,
3193
+ });
3194
+
3195
+ const settledPerpPosition = this.getPerpPositionWithLPSettle(
3196
+ perpPosition.marketIndex,
3197
+ perpPosition
3198
+ )[0];
3199
+
3200
+ const positionUnrealizedPnl = calculatePositionPNL(
3201
+ perpMarket,
3202
+ settledPerpPosition,
3203
+ true,
3204
+ oraclePriceData
3205
+ );
3206
+
3207
+ let pnlWeight;
3208
+ if (positionUnrealizedPnl.gt(ZERO)) {
3209
+ pnlWeight = calculateUnrealizedAssetWeight(
3210
+ perpMarket,
3211
+ quoteSpotMarket,
3212
+ positionUnrealizedPnl,
3213
+ marginCategory,
3214
+ oraclePriceData
3215
+ );
3216
+ } else {
3217
+ pnlWeight = SPOT_MARKET_WEIGHT_PRECISION;
3218
+ }
3219
+
3220
+ const pnlValue = positionUnrealizedPnl
3221
+ .mul(quoteOraclePriceData.price)
3222
+ .div(PRICE_PRECISION);
3223
+
3224
+ const wegithedPnlValue = pnlValue
3225
+ .mul(pnlWeight)
3226
+ .div(SPOT_MARKET_WEIGHT_PRECISION);
3227
+
3228
+ healthComponents.perpPnl.push({
3229
+ marketIndex: perpMarket.marketIndex,
3230
+ size: positionUnrealizedPnl,
3231
+ value: wegithedPnlValue,
3232
+ weight: pnlWeight,
3233
+ weightedValue: wegithedPnlValue,
3234
+ });
3235
+ }
3236
+
3237
+ let netQuoteValue = ZERO;
3238
+ for (const spotPosition of this.getActiveSpotPositions()) {
3239
+ const spotMarketAccount: SpotMarketAccount =
3240
+ this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
3241
+
3242
+ const oraclePriceData = this.getOracleDataForSpotMarket(
3243
+ spotPosition.marketIndex
3244
+ );
3245
+
3246
+ if (spotPosition.marketIndex === QUOTE_SPOT_MARKET_INDEX) {
3247
+ const tokenAmount = getSignedTokenAmount(
3248
+ getTokenAmount(
3249
+ spotPosition.scaledBalance,
3250
+ spotMarketAccount,
3251
+ spotPosition.balanceType
3252
+ ),
3253
+ spotPosition.balanceType
3254
+ );
3255
+
3256
+ netQuoteValue = netQuoteValue.add(tokenAmount);
3257
+ continue;
3258
+ }
3259
+
3260
+ const [worstCaseTokenAmount, worstCaseQuoteTokenAmount] =
3261
+ getWorstCaseTokenAmounts(
3262
+ spotPosition,
3263
+ spotMarketAccount,
3264
+ oraclePriceData
3265
+ );
3266
+
3267
+ netQuoteValue = netQuoteValue.add(worstCaseQuoteTokenAmount);
3268
+
3269
+ const baseAssetValue = getTokenValue(
3270
+ worstCaseTokenAmount.abs(),
3271
+ spotMarketAccount.decimals,
3272
+ oraclePriceData
3273
+ );
3274
+ const isLiability = isVariant(spotPosition.balanceType, 'borrow');
3275
+
3276
+ let weight;
3277
+ if (isLiability) {
3278
+ weight = calculateLiabilityWeight(
3279
+ worstCaseTokenAmount.abs(),
3280
+ spotMarketAccount,
3281
+ marginCategory
3282
+ );
3283
+ } else {
3284
+ weight = calculateAssetWeight(
3285
+ worstCaseTokenAmount,
3286
+ spotMarketAccount,
3287
+ marginCategory
3288
+ );
3289
+ }
3290
+
3291
+ const weightedValue = baseAssetValue
3292
+ .mul(weight)
3293
+ .div(SPOT_MARKET_WEIGHT_PRECISION);
3294
+
3295
+ if (isLiability) {
3296
+ healthComponents.borrows.push({
3297
+ marketIndex: spotMarketAccount.marketIndex,
3298
+ size: worstCaseTokenAmount,
3299
+ value: baseAssetValue,
3300
+ weight: weight,
3301
+ weightedValue: weightedValue,
3302
+ });
3303
+ } else {
3304
+ healthComponents.deposits.push({
3305
+ marketIndex: spotMarketAccount.marketIndex,
3306
+ size: worstCaseTokenAmount,
3307
+ value: weightedValue,
3308
+ weight: weight,
3309
+ weightedValue: weightedValue,
3310
+ });
3311
+ }
3312
+ }
3313
+
3314
+ if (!netQuoteValue.eq(ZERO)) {
3315
+ const spotMarketAccount = this.driftClient.getQuoteSpotMarketAccount();
3316
+ const oraclePriceData = this.getOracleDataForSpotMarket(
3317
+ QUOTE_SPOT_MARKET_INDEX
3318
+ );
3319
+
3320
+ const baseAssetValue = getTokenValue(
3321
+ netQuoteValue.abs(),
3322
+ spotMarketAccount.decimals,
3323
+ oraclePriceData
3324
+ );
3325
+ const isLiability = netQuoteValue.lt(ZERO);
3326
+
3327
+ let weight;
3328
+ if (isLiability) {
3329
+ weight = calculateLiabilityWeight(
3330
+ netQuoteValue.abs(),
3331
+ spotMarketAccount,
3332
+ marginCategory
3333
+ );
3334
+ } else {
3335
+ weight = calculateAssetWeight(
3336
+ netQuoteValue,
3337
+ spotMarketAccount,
3338
+ marginCategory
3339
+ );
3340
+ }
3341
+
3342
+ const weightedValue = baseAssetValue
3343
+ .mul(weight)
3344
+ .div(SPOT_MARKET_WEIGHT_PRECISION);
3345
+
3346
+ if (isLiability) {
3347
+ healthComponents.borrows.push({
3348
+ marketIndex: spotMarketAccount.marketIndex,
3349
+ size: netQuoteValue,
3350
+ value: baseAssetValue,
3351
+ weight: weight,
3352
+ weightedValue: weightedValue,
3353
+ });
3354
+ } else {
3355
+ healthComponents.deposits.push({
3356
+ marketIndex: spotMarketAccount.marketIndex,
3357
+ size: netQuoteValue,
3358
+ value: weightedValue,
3359
+ weight: weight,
3360
+ weightedValue: weightedValue,
3361
+ });
3362
+ }
3363
+ }
3364
+
3365
+ return healthComponents;
3366
+ }
3367
+
3120
3368
  /**
3121
3369
  * Get the total position value, excluding any position coming from the given target market
3122
3370
  * @param marketToIgnore