@drift-labs/sdk 2.31.1-beta.3 → 2.31.1-beta.5

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.31.1-beta.3
1
+ 2.31.1-beta.5
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.31.1-beta.2",
2
+ "version": "2.31.1-beta.4",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -0,0 +1,4 @@
1
+ import { PerpMarketAccount, SpotMarketAccount } from '../types';
2
+ export declare function getPerpMarketTierNumber(perpMarket: PerpMarketAccount): number;
3
+ export declare function getSpotMarketTierNumber(spotMarket: SpotMarketAccount): number;
4
+ export declare function perpTierIsAsSafeAs(perpTier: number, otherPerpTier: number, otherSpotTier: number): boolean;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.perpTierIsAsSafeAs = exports.getSpotMarketTierNumber = exports.getPerpMarketTierNumber = void 0;
4
+ const types_1 = require("../types");
5
+ function getPerpMarketTierNumber(perpMarket) {
6
+ if ((0, types_1.isVariant)(perpMarket.contractTier, 'a')) {
7
+ return 0;
8
+ }
9
+ else if ((0, types_1.isVariant)(perpMarket.contractTier, 'b')) {
10
+ return 1;
11
+ }
12
+ else if ((0, types_1.isVariant)(perpMarket.contractTier, 'c')) {
13
+ return 2;
14
+ }
15
+ else if ((0, types_1.isVariant)(perpMarket.contractTier, 'speculative')) {
16
+ return 3;
17
+ }
18
+ else if ((0, types_1.isVariant)(perpMarket.contractTier, 'isolated')) {
19
+ return 4;
20
+ }
21
+ else {
22
+ return 5;
23
+ }
24
+ }
25
+ exports.getPerpMarketTierNumber = getPerpMarketTierNumber;
26
+ function getSpotMarketTierNumber(spotMarket) {
27
+ if ((0, types_1.isVariant)(spotMarket.assetTier, 'collateral')) {
28
+ return 0;
29
+ }
30
+ else if ((0, types_1.isVariant)(spotMarket.assetTier, 'protected')) {
31
+ return 1;
32
+ }
33
+ else if ((0, types_1.isVariant)(spotMarket.assetTier, 'cross')) {
34
+ return 2;
35
+ }
36
+ else if ((0, types_1.isVariant)(spotMarket.assetTier, 'isolated')) {
37
+ return 3;
38
+ }
39
+ else if ((0, types_1.isVariant)(spotMarket.assetTier, 'unlisted')) {
40
+ return 4;
41
+ }
42
+ else {
43
+ return 5;
44
+ }
45
+ }
46
+ exports.getSpotMarketTierNumber = getSpotMarketTierNumber;
47
+ function perpTierIsAsSafeAs(perpTier, otherPerpTier, otherSpotTier) {
48
+ const asSafeAsPerp = perpTier <= otherPerpTier;
49
+ const asSafeAsSpot = otherSpotTier === 4 || (otherSpotTier >= 2 && perpTier <= 2);
50
+ return asSafeAsSpot && asSafeAsPerp;
51
+ }
52
+ exports.perpTierIsAsSafeAs = perpTierIsAsSafeAs;
package/lib/user.d.ts CHANGED
@@ -107,6 +107,7 @@ export declare class User {
107
107
  */
108
108
  getMaintenanceMarginRequirement(liquidationBuffer?: BN): BN;
109
109
  getActivePerpPositions(): PerpPosition[];
110
+ getActiveSpotPositions(): SpotPosition[];
110
111
  /**
111
112
  * calculates unrealized position price pnl
112
113
  * @returns : Precision QUOTE_PRECISION
@@ -185,7 +186,11 @@ export declare class User {
185
186
  * @returns : Precision TEN_THOUSAND
186
187
  */
187
188
  getMarginRatio(): BN;
188
- canBeLiquidated(): boolean;
189
+ canBeLiquidated(): {
190
+ canBeLiquidated: boolean;
191
+ marginRequirement: BN;
192
+ totalCollateral: BN;
193
+ };
189
194
  isBeingLiquidated(): boolean;
190
195
  isBankrupt(): boolean;
191
196
  /**
@@ -277,6 +282,10 @@ export declare class User {
277
282
  maxDepositAmount: BN;
278
283
  };
279
284
  canMakeIdle(slot: BN, slotsBeforeIdle: BN): boolean;
285
+ getSafestTiers(): {
286
+ perpTier: number;
287
+ spotTier: number;
288
+ };
280
289
  /**
281
290
  * Get the total position value, excluding any position coming from the given target market
282
291
  * @param marketToIgnore
package/lib/user.js CHANGED
@@ -12,6 +12,7 @@ const pollingUserAccountSubscriber_1 = require("./accounts/pollingUserAccountSub
12
12
  const webSocketUserAccountSubscriber_1 = require("./accounts/webSocketUserAccountSubscriber");
13
13
  const spotPosition_1 = require("./math/spotPosition");
14
14
  const oracles_1 = require("./math/oracles");
15
+ const tiers_1 = require("./math/tiers");
15
16
  class User {
16
17
  get isSubscribed() {
17
18
  return this._isSubscribed && this.accountSubscriber.isSubscribed;
@@ -308,6 +309,9 @@ class User {
308
309
  !(pos.openOrders == 0) ||
309
310
  !pos.lpShares.eq(numericConstants_1.ZERO));
310
311
  }
312
+ getActiveSpotPositions() {
313
+ return this.getUserAccount().spotPositions.filter((pos) => !(0, spotPosition_1.isSpotPositionAvailable)(pos));
314
+ }
311
315
  /**
312
316
  * calculates unrealized position price pnl
313
317
  * @returns : Precision QUOTE_PRECISION
@@ -778,12 +782,16 @@ class User {
778
782
  const totalCollateral = this.getTotalCollateral('Maintenance');
779
783
  // if user being liq'd, can continue to be liq'd until total collateral above the margin requirement plus buffer
780
784
  let liquidationBuffer = undefined;
781
- const isBeingLiquidated = (0, types_1.isVariant)(this.getUserAccount().status, 'beingLiquidated');
782
- if (isBeingLiquidated) {
785
+ if (this.isBeingLiquidated()) {
783
786
  liquidationBuffer = new _1.BN(this.driftClient.getStateAccount().liquidationMarginBufferRatio);
784
787
  }
785
- const maintenanceRequirement = this.getMaintenanceMarginRequirement(liquidationBuffer);
786
- return totalCollateral.lt(maintenanceRequirement);
788
+ const marginRequirement = this.getMaintenanceMarginRequirement(liquidationBuffer);
789
+ const canBeLiquidated = totalCollateral.lt(marginRequirement);
790
+ return {
791
+ canBeLiquidated,
792
+ marginRequirement,
793
+ totalCollateral,
794
+ };
787
795
  }
788
796
  isBeingLiquidated() {
789
797
  return (0, types_1.isOneOfVariant)(this.getUserAccount().status, [
@@ -1284,6 +1292,23 @@ class User {
1284
1292
  }
1285
1293
  return true;
1286
1294
  }
1295
+ getSafestTiers() {
1296
+ let safestPerpTier = 4;
1297
+ let safestSpotTier = 4;
1298
+ for (const perpPosition of this.getActivePerpPositions()) {
1299
+ safestPerpTier = Math.min(safestPerpTier, (0, tiers_1.getPerpMarketTierNumber)(this.driftClient.getPerpMarketAccount(perpPosition.marketIndex)));
1300
+ }
1301
+ for (const spotPosition of this.getActiveSpotPositions()) {
1302
+ if ((0, types_1.isVariant)(spotPosition.balanceType, 'deposit')) {
1303
+ continue;
1304
+ }
1305
+ safestSpotTier = Math.min(safestSpotTier, (0, tiers_1.getSpotMarketTierNumber)(this.driftClient.getSpotMarketAccount(spotPosition.marketIndex)));
1306
+ }
1307
+ return {
1308
+ perpTier: safestPerpTier,
1309
+ spotTier: safestSpotTier,
1310
+ };
1311
+ }
1287
1312
  /**
1288
1313
  * Get the total position value, excluding any position coming from the given target market
1289
1314
  * @param marketToIgnore
package/lib/userStats.js CHANGED
@@ -7,12 +7,15 @@ const webSocketUserStatsAccountSubsriber_1 = require("./accounts/webSocketUserSt
7
7
  const pda_1 = require("./addresses/pda");
8
8
  class UserStats {
9
9
  constructor(config) {
10
- var _a;
10
+ var _a, _b;
11
11
  this.driftClient = config.driftClient;
12
12
  this.userStatsAccountPublicKey = config.userStatsAccountPublicKey;
13
13
  if (((_a = config.accountSubscription) === null || _a === void 0 ? void 0 : _a.type) === 'polling') {
14
14
  this.accountSubscriber = new pollingUserStatsAccountSubscriber_1.PollingUserStatsAccountSubscriber(config.driftClient.program, config.userStatsAccountPublicKey, config.accountSubscription.accountLoader);
15
15
  }
16
+ else if (((_b = config.accountSubscription) === null || _b === void 0 ? void 0 : _b.type) === 'custom') {
17
+ throw new Error('Custom account subscription not yet implemented for user stats');
18
+ }
16
19
  else {
17
20
  this.accountSubscriber = new webSocketUserStatsAccountSubsriber_1.WebSocketUserStatsAccountSubscriber(config.driftClient.program, config.userStatsAccountPublicKey);
18
21
  }
@@ -11,4 +11,6 @@ export type UserStatsSubscriptionConfig = {
11
11
  } | {
12
12
  type: 'polling';
13
13
  accountLoader: BulkAccountLoader;
14
+ } | {
15
+ type: 'custom';
14
16
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.31.1-beta.3",
3
+ "version": "2.31.1-beta.5",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.31.1-beta.3",
2
+ "version": "2.31.1-beta.5",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -0,0 +1,44 @@
1
+ import { isVariant, PerpMarketAccount, SpotMarketAccount } from '../types';
2
+
3
+ export function getPerpMarketTierNumber(perpMarket: PerpMarketAccount): number {
4
+ if (isVariant(perpMarket.contractTier, 'a')) {
5
+ return 0;
6
+ } else if (isVariant(perpMarket.contractTier, 'b')) {
7
+ return 1;
8
+ } else if (isVariant(perpMarket.contractTier, 'c')) {
9
+ return 2;
10
+ } else if (isVariant(perpMarket.contractTier, 'speculative')) {
11
+ return 3;
12
+ } else if (isVariant(perpMarket.contractTier, 'isolated')) {
13
+ return 4;
14
+ } else {
15
+ return 5;
16
+ }
17
+ }
18
+
19
+ export function getSpotMarketTierNumber(spotMarket: SpotMarketAccount): number {
20
+ if (isVariant(spotMarket.assetTier, 'collateral')) {
21
+ return 0;
22
+ } else if (isVariant(spotMarket.assetTier, 'protected')) {
23
+ return 1;
24
+ } else if (isVariant(spotMarket.assetTier, 'cross')) {
25
+ return 2;
26
+ } else if (isVariant(spotMarket.assetTier, 'isolated')) {
27
+ return 3;
28
+ } else if (isVariant(spotMarket.assetTier, 'unlisted')) {
29
+ return 4;
30
+ } else {
31
+ return 5;
32
+ }
33
+ }
34
+
35
+ export function perpTierIsAsSafeAs(
36
+ perpTier: number,
37
+ otherPerpTier: number,
38
+ otherSpotTier: number
39
+ ): boolean {
40
+ const asSafeAsPerp = perpTier <= otherPerpTier;
41
+ const asSafeAsSpot =
42
+ otherSpotTier === 4 || (otherSpotTier >= 2 && perpTier <= 2);
43
+ return asSafeAsSpot && asSafeAsPerp;
44
+ }
package/src/user.ts CHANGED
@@ -72,6 +72,7 @@ import {
72
72
  } from './math/spotPosition';
73
73
 
74
74
  import { calculateLiveOracleTwap } from './math/oracles';
75
+ import { getPerpMarketTierNumber, getSpotMarketTierNumber } from './math/tiers';
75
76
 
76
77
  export class User {
77
78
  driftClient: DriftClient;
@@ -492,6 +493,12 @@ export class User {
492
493
  );
493
494
  }
494
495
 
496
+ public getActiveSpotPositions(): SpotPosition[] {
497
+ return this.getUserAccount().spotPositions.filter(
498
+ (pos) => !isSpotPositionAvailable(pos)
499
+ );
500
+ }
501
+
495
502
  /**
496
503
  * calculates unrealized position price pnl
497
504
  * @returns : Precision QUOTE_PRECISION
@@ -1437,24 +1444,29 @@ export class User {
1437
1444
  return netAssetValue.mul(TEN_THOUSAND).div(totalLiabilityValue);
1438
1445
  }
1439
1446
 
1440
- public canBeLiquidated(): boolean {
1447
+ public canBeLiquidated(): {
1448
+ canBeLiquidated: boolean;
1449
+ marginRequirement: BN;
1450
+ totalCollateral: BN;
1451
+ } {
1441
1452
  const totalCollateral = this.getTotalCollateral('Maintenance');
1442
1453
 
1443
1454
  // if user being liq'd, can continue to be liq'd until total collateral above the margin requirement plus buffer
1444
1455
  let liquidationBuffer = undefined;
1445
- const isBeingLiquidated = isVariant(
1446
- this.getUserAccount().status,
1447
- 'beingLiquidated'
1448
- );
1449
-
1450
- if (isBeingLiquidated) {
1456
+ if (this.isBeingLiquidated()) {
1451
1457
  liquidationBuffer = new BN(
1452
1458
  this.driftClient.getStateAccount().liquidationMarginBufferRatio
1453
1459
  );
1454
1460
  }
1455
- const maintenanceRequirement =
1461
+ const marginRequirement =
1456
1462
  this.getMaintenanceMarginRequirement(liquidationBuffer);
1457
- return totalCollateral.lt(maintenanceRequirement);
1463
+ const canBeLiquidated = totalCollateral.lt(marginRequirement);
1464
+
1465
+ return {
1466
+ canBeLiquidated,
1467
+ marginRequirement,
1468
+ totalCollateral,
1469
+ };
1458
1470
  }
1459
1471
 
1460
1472
  public isBeingLiquidated(): boolean {
@@ -2306,6 +2318,38 @@ export class User {
2306
2318
  return true;
2307
2319
  }
2308
2320
 
2321
+ public getSafestTiers(): { perpTier: number; spotTier: number } {
2322
+ let safestPerpTier = 4;
2323
+ let safestSpotTier = 4;
2324
+
2325
+ for (const perpPosition of this.getActivePerpPositions()) {
2326
+ safestPerpTier = Math.min(
2327
+ safestPerpTier,
2328
+ getPerpMarketTierNumber(
2329
+ this.driftClient.getPerpMarketAccount(perpPosition.marketIndex)
2330
+ )
2331
+ );
2332
+ }
2333
+
2334
+ for (const spotPosition of this.getActiveSpotPositions()) {
2335
+ if (isVariant(spotPosition.balanceType, 'deposit')) {
2336
+ continue;
2337
+ }
2338
+
2339
+ safestSpotTier = Math.min(
2340
+ safestSpotTier,
2341
+ getSpotMarketTierNumber(
2342
+ this.driftClient.getSpotMarketAccount(spotPosition.marketIndex)
2343
+ )
2344
+ );
2345
+ }
2346
+
2347
+ return {
2348
+ perpTier: safestPerpTier,
2349
+ spotTier: safestSpotTier,
2350
+ };
2351
+ }
2352
+
2309
2353
  /**
2310
2354
  * Get the total position value, excluding any position coming from the given target market
2311
2355
  * @param marketToIgnore
@@ -2347,6 +2391,7 @@ export class User {
2347
2391
 
2348
2392
  return oracleData;
2349
2393
  }
2394
+
2350
2395
  private getOracleDataForSpotMarket(marketIndex: number): OraclePriceData {
2351
2396
  const oracleKey = this.driftClient.getSpotMarketAccount(marketIndex).oracle;
2352
2397
 
package/src/userStats.ts CHANGED
@@ -25,6 +25,10 @@ export class UserStats {
25
25
  config.userStatsAccountPublicKey,
26
26
  config.accountSubscription.accountLoader
27
27
  );
28
+ } else if (config.accountSubscription?.type === 'custom') {
29
+ throw new Error(
30
+ 'Custom account subscription not yet implemented for user stats'
31
+ );
28
32
  } else {
29
33
  this.accountSubscriber = new WebSocketUserStatsAccountSubscriber(
30
34
  config.driftClient.program,
@@ -15,4 +15,7 @@ export type UserStatsSubscriptionConfig =
15
15
  | {
16
16
  type: 'polling';
17
17
  accountLoader: BulkAccountLoader;
18
+ }
19
+ | {
20
+ type: 'custom';
18
21
  };