@drift-labs/sdk 2.52.0-beta.0 → 2.52.0-beta.10

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 (100) hide show
  1. package/VERSION +1 -1
  2. package/examples/phoenix.ts +11 -4
  3. package/lib/accounts/basicUserAccountSubscriber.d.ts +4 -0
  4. package/lib/accounts/basicUserAccountSubscriber.js +4 -0
  5. package/lib/accounts/bulkAccountLoader.js +7 -1
  6. package/lib/accounts/oneShotUserAccountSubscriber.d.ts +17 -0
  7. package/lib/accounts/oneShotUserAccountSubscriber.js +48 -0
  8. package/lib/accounts/pollingInsuranceFundStakeAccountSubscriber.js +1 -0
  9. package/lib/accounts/webSocketAccountSubscriber.d.ts +1 -1
  10. package/lib/accounts/webSocketAccountSubscriber.js +7 -4
  11. package/lib/accounts/webSocketProgramAccountSubscriber.d.ts +1 -1
  12. package/lib/accounts/webSocketProgramAccountSubscriber.js +7 -4
  13. package/lib/adminClient.d.ts +1 -0
  14. package/lib/adminClient.js +9 -0
  15. package/lib/constants/numericConstants.d.ts +3 -0
  16. package/lib/constants/numericConstants.js +4 -1
  17. package/lib/dlob/orderBookLevels.d.ts +22 -0
  18. package/lib/dlob/orderBookLevels.js +115 -1
  19. package/lib/driftClient.d.ts +3 -0
  20. package/lib/driftClient.js +31 -5
  21. package/lib/events/webSocketLogProvider.js +3 -3
  22. package/lib/factory/bigNum.d.ts +1 -1
  23. package/lib/factory/bigNum.js +5 -2
  24. package/lib/idl/drift.json +52 -1
  25. package/lib/index.d.ts +2 -1
  26. package/lib/index.js +2 -1
  27. package/lib/math/amm.d.ts +5 -1
  28. package/lib/math/amm.js +62 -13
  29. package/lib/math/state.js +3 -0
  30. package/lib/orderSubscriber/OrderSubscriber.js +1 -0
  31. package/lib/orderSubscriber/WebsocketSubscription.d.ts +4 -1
  32. package/lib/orderSubscriber/WebsocketSubscription.js +25 -1
  33. package/lib/orderSubscriber/types.d.ts +1 -0
  34. package/lib/phoenix/phoenixSubscriber.js +10 -6
  35. package/lib/priorityFee/averageOverSlotsStrategy.d.ts +12 -0
  36. package/lib/priorityFee/averageOverSlotsStrategy.js +28 -0
  37. package/lib/priorityFee/averageStrategy.d.ts +7 -0
  38. package/lib/priorityFee/averageStrategy.js +11 -0
  39. package/lib/priorityFee/ewmaStrategy.d.ts +13 -0
  40. package/lib/priorityFee/ewmaStrategy.js +33 -0
  41. package/lib/priorityFee/index.d.ts +7 -0
  42. package/lib/priorityFee/index.js +23 -0
  43. package/lib/priorityFee/maxOverSlotsStrategy.d.ts +12 -0
  44. package/lib/priorityFee/maxOverSlotsStrategy.js +29 -0
  45. package/lib/priorityFee/maxStrategy.d.ts +7 -0
  46. package/lib/priorityFee/maxStrategy.js +9 -0
  47. package/lib/priorityFee/priorityFeeSubscriber.d.ts +15 -4
  48. package/lib/priorityFee/priorityFeeSubscriber.js +38 -14
  49. package/lib/priorityFee/types.d.ts +6 -0
  50. package/lib/priorityFee/types.js +2 -0
  51. package/lib/slot/SlotSubscriber.d.ts +11 -3
  52. package/lib/slot/SlotSubscriber.js +40 -4
  53. package/lib/types.d.ts +4 -1
  54. package/lib/types.js +1 -0
  55. package/lib/user.d.ts +1 -1
  56. package/lib/user.js +15 -8
  57. package/lib/userMap/userMap.d.ts +3 -0
  58. package/lib/userMap/userMap.js +10 -1
  59. package/lib/userStats.d.ts +1 -0
  60. package/lib/userStats.js +3 -0
  61. package/package.json +1 -1
  62. package/src/accounts/basicUserAccountSubscriber.ts +4 -0
  63. package/src/accounts/bulkAccountLoader.ts +10 -3
  64. package/src/accounts/oneShotUserAccountSubscriber.ts +64 -0
  65. package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +1 -0
  66. package/src/accounts/webSocketAccountSubscriber.ts +7 -4
  67. package/src/accounts/webSocketProgramAccountSubscriber.ts +7 -4
  68. package/src/adminClient.ts +13 -0
  69. package/src/constants/numericConstants.ts +4 -0
  70. package/src/dlob/orderBookLevels.ts +136 -0
  71. package/src/driftClient.ts +50 -6
  72. package/src/events/webSocketLogProvider.ts +3 -3
  73. package/src/factory/bigNum.ts +11 -2
  74. package/src/idl/drift.json +52 -1
  75. package/src/index.ts +2 -1
  76. package/src/math/amm.ts +159 -25
  77. package/src/math/state.ts +3 -0
  78. package/src/orderSubscriber/OrderSubscriber.ts +1 -0
  79. package/src/orderSubscriber/WebsocketSubscription.ts +28 -0
  80. package/src/orderSubscriber/types.ts +1 -0
  81. package/src/phoenix/phoenixSubscriber.ts +14 -8
  82. package/src/priorityFee/averageOverSlotsStrategy.ts +30 -0
  83. package/src/priorityFee/averageStrategy.ts +11 -0
  84. package/src/priorityFee/ewmaStrategy.ts +40 -0
  85. package/src/priorityFee/index.ts +7 -0
  86. package/src/priorityFee/maxOverSlotsStrategy.ts +31 -0
  87. package/src/priorityFee/maxStrategy.ts +7 -0
  88. package/src/priorityFee/priorityFeeSubscriber.ts +46 -19
  89. package/src/priorityFee/types.ts +5 -0
  90. package/src/slot/SlotSubscriber.ts +52 -5
  91. package/src/types.ts +2 -1
  92. package/src/user.ts +25 -8
  93. package/src/userMap/userMap.ts +17 -3
  94. package/src/userStats.ts +8 -0
  95. package/tests/amm/test.ts +219 -11
  96. package/tests/bn/test.ts +27 -0
  97. package/tests/dlob/helpers.ts +1 -1
  98. package/tests/dlob/test.ts +372 -2
  99. package/tests/tx/priorityFeeStrategy.ts +97 -0
  100. package/tests/user/helpers.ts +1 -0
@@ -1,18 +1,40 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PriorityFeeSubscriber = void 0;
4
+ const averageOverSlotsStrategy_1 = require("./averageOverSlotsStrategy");
5
+ const maxOverSlotsStrategy_1 = require("./maxOverSlotsStrategy");
4
6
  class PriorityFeeSubscriber {
5
- constructor({ connection, frequencyMs, addresses, slotsToCheck = 10, }) {
7
+ constructor({ connection, frequencyMs, addresses, customStrategy, slotsToCheck = 10, }) {
8
+ this.averageStrategy = new averageOverSlotsStrategy_1.AverageOverSlotsStrategy();
9
+ this.maxStrategy = new maxOverSlotsStrategy_1.MaxOverSlotsStrategy();
6
10
  this.latestPriorityFee = 0;
7
- // avg of last {slotsToCheck} slots
8
- this.avgPriorityFee = 0;
9
- // max of last {slotsToCheck} slots
10
- this.maxPriorityFee = 0;
11
+ this.lastStrategyResult = 0;
12
+ this.lastCustomStrategyResult = 0;
13
+ this.lastAvgStrategyResult = 0;
14
+ this.lastMaxStrategyResult = 0;
11
15
  this.lastSlotSeen = 0;
12
16
  this.connection = connection;
13
17
  this.frequencyMs = frequencyMs;
14
18
  this.addresses = addresses;
15
- this.slotsToCheck = slotsToCheck;
19
+ if (slotsToCheck) {
20
+ this.averageStrategy = new averageOverSlotsStrategy_1.AverageOverSlotsStrategy(slotsToCheck);
21
+ this.maxStrategy = new maxOverSlotsStrategy_1.MaxOverSlotsStrategy(slotsToCheck);
22
+ }
23
+ if (customStrategy) {
24
+ this.customStrategy = customStrategy;
25
+ }
26
+ }
27
+ get avgPriorityFee() {
28
+ return this.lastAvgStrategyResult;
29
+ }
30
+ get maxPriorityFee() {
31
+ return this.lastMaxStrategyResult;
32
+ }
33
+ get customPriorityFee() {
34
+ if (!this.customStrategy) {
35
+ console.error('Custom strategy not set');
36
+ }
37
+ return this.lastCustomStrategyResult;
16
38
  }
17
39
  async subscribe() {
18
40
  if (this.intervalId) {
@@ -21,20 +43,22 @@ class PriorityFeeSubscriber {
21
43
  this.intervalId = setInterval(this.load.bind(this), this.frequencyMs);
22
44
  }
23
45
  async load() {
24
- var _a, _b, _c;
25
46
  // @ts-ignore
26
47
  const rpcJSONResponse = await this.connection._rpcRequest('getRecentPrioritizationFees', [this.addresses]);
27
- const descResults = (_c = (_b = (_a = rpcJSONResponse === null || rpcJSONResponse === void 0 ? void 0 : rpcJSONResponse.result) === null || _a === void 0 ? void 0 : _a.sort((a, b) => b.slot - a.slot)) === null || _b === void 0 ? void 0 : _b.slice(0, this.slotsToCheck)) !== null && _c !== void 0 ? _c : [];
28
- if (!(descResults === null || descResults === void 0 ? void 0 : descResults.length))
48
+ // getRecentPrioritizationFees returns results unsorted
49
+ const results = rpcJSONResponse === null || rpcJSONResponse === void 0 ? void 0 : rpcJSONResponse.result;
50
+ if (!results.length)
29
51
  return;
52
+ const descResults = results.sort((a, b) => b.slot - a.slot);
30
53
  const mostRecentResult = descResults[0];
31
54
  this.latestPriorityFee = mostRecentResult.prioritizationFee;
32
55
  this.lastSlotSeen = mostRecentResult.slot;
33
- this.avgPriorityFee =
34
- descResults.reduce((a, b) => {
35
- return a + b.prioritizationFee;
36
- }, 0) / descResults.length;
37
- this.maxPriorityFee = Math.max(...descResults.map((result) => result.prioritizationFee));
56
+ this.lastAvgStrategyResult = this.averageStrategy.calculate(descResults);
57
+ this.lastMaxStrategyResult = this.maxStrategy.calculate(descResults);
58
+ if (this.customStrategy) {
59
+ this.lastCustomStrategyResult =
60
+ this.customStrategy.calculate(descResults);
61
+ }
38
62
  }
39
63
  async unsubscribe() {
40
64
  if (this.intervalId) {
@@ -0,0 +1,6 @@
1
+ export interface PriorityFeeStrategy {
2
+ calculate(samples: {
3
+ slot: number;
4
+ prioritizationFee: number;
5
+ }[]): number;
6
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,8 +1,11 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
2
3
  import { Connection } from '@solana/web3.js';
3
4
  import { EventEmitter } from 'events';
4
5
  import StrictEventEmitter from 'strict-event-emitter-types/types/src';
5
- type SlotSubscriberConfig = {};
6
+ type SlotSubscriberConfig = {
7
+ resubTimeoutMs?: number;
8
+ };
6
9
  export interface SlotSubscriberEvents {
7
10
  newSlot: (newSlot: number) => void;
8
11
  }
@@ -11,9 +14,14 @@ export declare class SlotSubscriber {
11
14
  currentSlot: number;
12
15
  subscriptionId: number;
13
16
  eventEmitter: StrictEventEmitter<EventEmitter, SlotSubscriberEvents>;
14
- constructor(connection: Connection, _config?: SlotSubscriberConfig);
17
+ timeoutId?: NodeJS.Timeout;
18
+ resubTimeoutMs?: number;
19
+ isUnsubscribing: boolean;
20
+ receivingData: boolean;
21
+ constructor(connection: Connection, config?: SlotSubscriberConfig);
15
22
  subscribe(): Promise<void>;
23
+ private setTimeout;
16
24
  getSlot(): number;
17
- unsubscribe(): Promise<void>;
25
+ unsubscribe(onResub?: boolean): Promise<void>;
18
26
  }
19
27
  export {};
@@ -3,28 +3,64 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SlotSubscriber = void 0;
4
4
  const events_1 = require("events");
5
5
  class SlotSubscriber {
6
- constructor(connection, _config) {
6
+ constructor(connection, config) {
7
7
  this.connection = connection;
8
+ this.isUnsubscribing = false;
9
+ this.receivingData = false;
8
10
  this.eventEmitter = new events_1.EventEmitter();
11
+ this.resubTimeoutMs = config === null || config === void 0 ? void 0 : config.resubTimeoutMs;
9
12
  }
10
13
  async subscribe() {
11
- if (this.subscriptionId) {
14
+ if (this.subscriptionId != null) {
12
15
  return;
13
16
  }
14
17
  this.currentSlot = await this.connection.getSlot('confirmed');
15
18
  this.subscriptionId = this.connection.onSlotChange((slotInfo) => {
16
19
  if (!this.currentSlot || this.currentSlot < slotInfo.slot) {
20
+ if (this.resubTimeoutMs && !this.isUnsubscribing) {
21
+ this.receivingData = true;
22
+ clearTimeout(this.timeoutId);
23
+ this.setTimeout();
24
+ }
17
25
  this.currentSlot = slotInfo.slot;
18
26
  this.eventEmitter.emit('newSlot', slotInfo.slot);
19
27
  }
20
28
  });
29
+ if (this.resubTimeoutMs) {
30
+ this.setTimeout();
31
+ }
32
+ }
33
+ setTimeout() {
34
+ this.timeoutId = setTimeout(async () => {
35
+ if (this.isUnsubscribing) {
36
+ // If we are in the process of unsubscribing, do not attempt to resubscribe
37
+ return;
38
+ }
39
+ if (this.receivingData) {
40
+ console.log(`No new slot in ${this.resubTimeoutMs}ms, slot subscriber resubscribing`);
41
+ await this.unsubscribe(true);
42
+ this.receivingData = false;
43
+ await this.subscribe();
44
+ }
45
+ }, this.resubTimeoutMs);
21
46
  }
22
47
  getSlot() {
23
48
  return this.currentSlot;
24
49
  }
25
- async unsubscribe() {
26
- if (this.subscriptionId) {
50
+ async unsubscribe(onResub = false) {
51
+ if (!onResub) {
52
+ this.resubTimeoutMs = undefined;
53
+ }
54
+ this.isUnsubscribing = true;
55
+ clearTimeout(this.timeoutId);
56
+ this.timeoutId = undefined;
57
+ if (this.subscriptionId != null) {
27
58
  await this.connection.removeSlotChangeListener(this.subscriptionId);
59
+ this.subscriptionId = undefined;
60
+ this.isUnsubscribing = false;
61
+ }
62
+ else {
63
+ this.isUnsubscribing = false;
28
64
  }
29
65
  }
30
66
  }
package/lib/types.d.ts CHANGED
@@ -278,6 +278,9 @@ export declare class DepositExplanation {
278
278
  static readonly TRANSFER: {
279
279
  transfer: {};
280
280
  };
281
+ static readonly BORROW: {
282
+ borrow: {};
283
+ };
281
284
  }
282
285
  export declare class SettlePnlExplanation {
283
286
  static readonly NONE: {
@@ -767,7 +770,7 @@ export type AMM = {
767
770
  pegMultiplier: BN;
768
771
  cumulativeFundingRateLong: BN;
769
772
  cumulativeFundingRateShort: BN;
770
- last24hAvgFundingRate: BN;
773
+ last24HAvgFundingRate: BN;
771
774
  lastFundingRateShort: BN;
772
775
  lastFundingRateLong: BN;
773
776
  totalLiquidationFee: BN;
package/lib/types.js CHANGED
@@ -183,6 +183,7 @@ class DepositExplanation {
183
183
  exports.DepositExplanation = DepositExplanation;
184
184
  DepositExplanation.NONE = { none: {} };
185
185
  DepositExplanation.TRANSFER = { transfer: {} };
186
+ DepositExplanation.BORROW = { borrow: {} };
186
187
  class SettlePnlExplanation {
187
188
  }
188
189
  exports.SettlePnlExplanation = SettlePnlExplanation;
package/lib/user.d.ts CHANGED
@@ -353,7 +353,7 @@ export declare class User {
353
353
  * @param quoteAmount
354
354
  * @returns feeForQuote : Precision QUOTE_PRECISION
355
355
  */
356
- calculateFeeForQuoteAmount(quoteAmount: BN): BN;
356
+ calculateFeeForQuoteAmount(quoteAmount: BN, marketIndex?: number): BN;
357
357
  /**
358
358
  * Calculates a user's max withdrawal amounts for a spot market. If reduceOnly is true,
359
359
  * it will return the max withdrawal amount without opening a liability for the user
package/lib/user.js CHANGED
@@ -768,8 +768,7 @@ class User {
768
768
  * @returns : Precision QUOTE_PRECISION
769
769
  */
770
770
  getPerpPositionValue(marketIndex, oraclePriceData, includeOpenOrders = false) {
771
- const userPosition = this.getPerpPositionWithLPSettle(marketIndex)[0] ||
772
- this.getEmptyPosition(marketIndex);
771
+ const userPosition = this.getPerpPositionWithLPSettle(marketIndex, undefined, false, true)[0] || this.getEmptyPosition(marketIndex);
773
772
  const market = this.driftClient.getPerpMarketAccount(userPosition.marketIndex);
774
773
  return (0, margin_1.calculateBaseAssetValueWithOracle)(market, userPosition, oraclePriceData, includeOpenOrders);
775
774
  }
@@ -1645,7 +1644,7 @@ class User {
1645
1644
  const feeTier = this.getUserFeeTier(marketType);
1646
1645
  let takerFee = feeTier.feeNumerator / feeTier.feeDenominator;
1647
1646
  let makerFee = feeTier.makerRebateNumerator / feeTier.makerRebateDenominator;
1648
- if (marketIndex && (0, types_1.isVariant)(marketType, 'perp')) {
1647
+ if (marketIndex !== undefined && (0, types_1.isVariant)(marketType, 'perp')) {
1649
1648
  const marketAccount = this.driftClient.getPerpMarketAccount(marketIndex);
1650
1649
  takerFee += (takerFee * marketAccount.feeAdjustment) / 100;
1651
1650
  makerFee += (makerFee * marketAccount.feeAdjustment) / 100;
@@ -1660,11 +1659,19 @@ class User {
1660
1659
  * @param quoteAmount
1661
1660
  * @returns feeForQuote : Precision QUOTE_PRECISION
1662
1661
  */
1663
- calculateFeeForQuoteAmount(quoteAmount) {
1664
- const feeTier = this.getUserFeeTier(_1.MarketType.PERP);
1665
- return quoteAmount
1666
- .mul(new _1.BN(feeTier.feeNumerator))
1667
- .div(new _1.BN(feeTier.feeDenominator));
1662
+ calculateFeeForQuoteAmount(quoteAmount, marketIndex) {
1663
+ if (marketIndex !== undefined) {
1664
+ const takerFeeMultiplier = this.getMarketFees(_1.MarketType.PERP, marketIndex).takerFee;
1665
+ const feeAmountNum = _1.BigNum.from(quoteAmount, numericConstants_1.QUOTE_PRECISION_EXP).toNum() *
1666
+ takerFeeMultiplier;
1667
+ return _1.BigNum.fromPrint(feeAmountNum.toString(), numericConstants_1.QUOTE_PRECISION_EXP).val;
1668
+ }
1669
+ else {
1670
+ const feeTier = this.getUserFeeTier(_1.MarketType.PERP);
1671
+ return quoteAmount
1672
+ .mul(new _1.BN(feeTier.feeNumerator))
1673
+ .div(new _1.BN(feeTier.feeDenominator));
1674
+ }
1668
1675
  }
1669
1676
  /**
1670
1677
  * Calculates a user's max withdrawal amounts for a spot market. If reduceOnly is true,
@@ -22,6 +22,7 @@ export declare class UserMap implements UserMapInterface {
22
22
  private subscription;
23
23
  private stateAccountUpdateCallback;
24
24
  private decode;
25
+ private mostRecentSlot;
25
26
  private syncPromise?;
26
27
  private syncPromiseResolver;
27
28
  /**
@@ -68,4 +69,6 @@ export declare class UserMap implements UserMapInterface {
68
69
  sync(): Promise<void>;
69
70
  unsubscribe(): Promise<void>;
70
71
  updateUserAccount(key: string, userAccount: UserAccount, slot: number): Promise<void>;
72
+ updateLatestSlot(slot: number): void;
73
+ getSlot(): number;
71
74
  }
@@ -21,6 +21,7 @@ class UserMap {
21
21
  this.lastNumberOfSubAccounts = state.numberOfSubAccounts;
22
22
  }
23
23
  };
24
+ this.mostRecentSlot = 0;
24
25
  this.driftClient = config.driftClient;
25
26
  if (config.connection) {
26
27
  this.connection = config.connection;
@@ -73,7 +74,7 @@ class UserMap {
73
74
  userAccountPublicKey,
74
75
  accountSubscription: {
75
76
  type: 'custom',
76
- userAccountSubscriber: new __1.BasicUserAccountSubscriber(userAccountPublicKey, userAccount, slot),
77
+ userAccountSubscriber: new __1.OneShotUserAccountSubscriber(this.driftClient.program, userAccountPublicKey, userAccount, slot, this.commitment),
77
78
  },
78
79
  });
79
80
  await user.subscribe(userAccount);
@@ -218,6 +219,7 @@ class UserMap {
218
219
  await this.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
219
220
  const rpcResponseAndContext = rpcJSONResponse.result;
220
221
  const slot = rpcResponseAndContext.context.slot;
222
+ this.updateLatestSlot(slot);
221
223
  const programAccountBufferMap = new Map();
222
224
  for (const programAccount of rpcResponseAndContext.value) {
223
225
  programAccountBufferMap.set(programAccount.pubkey.toString(),
@@ -263,6 +265,7 @@ class UserMap {
263
265
  }
264
266
  }
265
267
  async updateUserAccount(key, userAccount, slot) {
268
+ this.updateLatestSlot(slot);
266
269
  if (!this.userMap.has(key)) {
267
270
  this.addPubkey(new web3_js_1.PublicKey(key), userAccount, slot);
268
271
  }
@@ -271,5 +274,11 @@ class UserMap {
271
274
  user.accountSubscriber.updateData(userAccount, slot);
272
275
  }
273
276
  }
277
+ updateLatestSlot(slot) {
278
+ this.mostRecentSlot = Math.max(slot, this.mostRecentSlot);
279
+ }
280
+ getSlot() {
281
+ return this.mostRecentSlot;
282
+ }
274
283
  }
275
284
  exports.UserMap = UserMap;
@@ -15,4 +15,5 @@ export declare class UserStats {
15
15
  getAccountAndSlot(): DataAndSlot<UserStatsAccount>;
16
16
  getAccount(): UserStatsAccount;
17
17
  getReferrerInfo(): ReferrerInfo | undefined;
18
+ static getOldestActionTs(account: UserStatsAccount): number;
18
19
  }
package/lib/userStats.js CHANGED
@@ -48,5 +48,8 @@ class UserStats {
48
48
  };
49
49
  }
50
50
  }
51
+ static getOldestActionTs(account) {
52
+ return Math.min(account.lastFillerVolume30DTs.toNumber(), account.lastMakerVolume30DTs.toNumber(), account.lastTakerVolume30DTs.toNumber());
53
+ }
51
54
  }
52
55
  exports.UserStats = UserStats;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.52.0-beta.0",
3
+ "version": "2.52.0-beta.10",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -4,6 +4,10 @@ import StrictEventEmitter from 'strict-event-emitter-types';
4
4
  import { EventEmitter } from 'events';
5
5
  import { UserAccount } from '../types';
6
6
 
7
+ /**
8
+ * Basic implementation of UserAccountSubscriber. It will only take in UserAccount
9
+ * data during initialization and will not fetch or subscribe to updates.
10
+ */
7
11
  export class BasicUserAccountSubscriber implements UserAccountSubscriber {
8
12
  isSubscribed: boolean;
9
13
  eventEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents>;
@@ -78,6 +78,7 @@ export class BulkAccountLoader {
78
78
  if (existingAccountToLoad) {
79
79
  existingAccountToLoad.callbacks.delete(callbackId);
80
80
  if (existingAccountToLoad.callbacks.size === 0) {
81
+ this.bufferAndSlotMap.delete(publicKey.toString());
81
82
  this.accountsToLoad.delete(existingAccountToLoad.publicKey.toString());
82
83
  }
83
84
  }
@@ -153,9 +154,11 @@ export class BulkAccountLoader {
153
154
  const requests = new Array<{ methodName: string; args: any }>();
154
155
  for (const accountsToLoadChunk of accountsToLoadChunks) {
155
156
  const args = [
156
- accountsToLoadChunk.map((accountToLoad) => {
157
- return accountToLoad.publicKey.toBase58();
158
- }),
157
+ accountsToLoadChunk
158
+ .filter((accountToLoad) => accountToLoad.callbacks.size > 0)
159
+ .map((accountToLoad) => {
160
+ return accountToLoad.publicKey.toBase58();
161
+ }),
159
162
  { commitment: this.commitment },
160
163
  ];
161
164
 
@@ -190,6 +193,10 @@ export class BulkAccountLoader {
190
193
 
191
194
  const accountsToLoad = accountsToLoadChunks[i];
192
195
  accountsToLoad.forEach((accountToLoad, j) => {
196
+ if (accountToLoad.callbacks.size === 0) {
197
+ return;
198
+ }
199
+
193
200
  const key = accountToLoad.publicKey.toBase58();
194
201
  const oldRPCResponse = this.bufferAndSlotMap.get(key);
195
202
 
@@ -0,0 +1,64 @@
1
+ import { Commitment, PublicKey } from '@solana/web3.js';
2
+ import { UserAccount } from '../types';
3
+ import { BasicUserAccountSubscriber } from './basicUserAccountSubscriber';
4
+ import { Program } from '@coral-xyz/anchor';
5
+
6
+ /**
7
+ * Simple implementation of UserAccountSubscriber. It will fetch the UserAccount
8
+ * date on subscribe (or call to fetch) if no account data is provided on init.
9
+ * Expect to use only 1 RPC call unless you call fetch repeatedly.
10
+ */
11
+ export class OneShotUserAccountSubscriber extends BasicUserAccountSubscriber {
12
+ program: Program;
13
+ commitment: Commitment;
14
+
15
+ public constructor(
16
+ program: Program,
17
+ userAccountPublicKey: PublicKey,
18
+ data?: UserAccount,
19
+ slot?: number,
20
+ commitment?: Commitment
21
+ ) {
22
+ super(userAccountPublicKey, data, slot);
23
+ this.program = program;
24
+ this.commitment = commitment ?? 'confirmed';
25
+ }
26
+
27
+ async subscribe(userAccount?: UserAccount): Promise<boolean> {
28
+ if (userAccount) {
29
+ this.user = { data: userAccount, slot: this.user.slot };
30
+ return true;
31
+ }
32
+
33
+ await this.fetchIfUnloaded();
34
+ if (this.doesAccountExist()) {
35
+ this.eventEmitter.emit('update');
36
+ }
37
+ return true;
38
+ }
39
+
40
+ async fetchIfUnloaded(): Promise<void> {
41
+ if (this.user.data === undefined) {
42
+ await this.fetch();
43
+ }
44
+ }
45
+
46
+ async fetch(): Promise<void> {
47
+ try {
48
+ const dataAndContext = await this.program.account.user.fetchAndContext(
49
+ this.userAccountPublicKey,
50
+ this.commitment
51
+ );
52
+ if (dataAndContext.context.slot > (this.user?.slot ?? 0)) {
53
+ this.user = {
54
+ data: dataAndContext.data as UserAccount,
55
+ slot: dataAndContext.context.slot,
56
+ };
57
+ }
58
+ } catch (e) {
59
+ console.error(
60
+ `OneShotUserAccountSubscriber.fetch() UserAccount does not exist: ${e.message}`
61
+ );
62
+ }
63
+ }
64
+ }
@@ -54,6 +54,7 @@ export class PollingInsuranceFundStakeAccountSubscriber
54
54
 
55
55
  await this.addToAccountLoader();
56
56
 
57
+ await this.fetchIfUnloaded();
57
58
  if (this.doesAccountExist()) {
58
59
  this.eventEmitter.emit('update');
59
60
  }
@@ -40,7 +40,7 @@ export class WebSocketAccountSubscriber<T> implements AccountSubscriber<T> {
40
40
  }
41
41
 
42
42
  async subscribe(onChange: (data: T) => void): Promise<void> {
43
- if (this.listenerId || this.isUnsubscribing) {
43
+ if (this.listenerId != null || this.isUnsubscribing) {
44
44
  return;
45
45
  }
46
46
 
@@ -95,7 +95,7 @@ export class WebSocketAccountSubscriber<T> implements AccountSubscriber<T> {
95
95
  console.log(
96
96
  `No ws data from ${this.accountName} in ${this.resubTimeoutMs}ms, resubscribing`
97
97
  );
98
- await this.unsubscribe();
98
+ await this.unsubscribe(true);
99
99
  this.receivingData = false;
100
100
  await this.subscribe(this.onChange);
101
101
  }
@@ -164,12 +164,15 @@ export class WebSocketAccountSubscriber<T> implements AccountSubscriber<T> {
164
164
  }
165
165
  }
166
166
 
167
- unsubscribe(): Promise<void> {
167
+ unsubscribe(onResub = false): Promise<void> {
168
+ if (!onResub) {
169
+ this.resubTimeoutMs = undefined;
170
+ }
168
171
  this.isUnsubscribing = true;
169
172
  clearTimeout(this.timeoutId);
170
173
  this.timeoutId = undefined;
171
174
 
172
- if (this.listenerId) {
175
+ if (this.listenerId != null) {
173
176
  const promise = this.program.provider.connection
174
177
  .removeAccountChangeListener(this.listenerId)
175
178
  .then(() => {
@@ -49,7 +49,7 @@ export class WebSocketProgramAccountSubscriber<T>
49
49
  async subscribe(
50
50
  onChange: (accountId: PublicKey, data: T, context: Context) => void
51
51
  ): Promise<void> {
52
- if (this.listenerId || this.isUnsubscribing) {
52
+ if (this.listenerId != null || this.isUnsubscribing) {
53
53
  return;
54
54
  }
55
55
 
@@ -91,7 +91,7 @@ export class WebSocketProgramAccountSubscriber<T>
91
91
  console.log(
92
92
  `No ws data from ${this.subscriptionName} in ${this.resubTimeoutMs}ms, resubscribing`
93
93
  );
94
- await this.unsubscribe();
94
+ await this.unsubscribe(true);
95
95
  this.receivingData = false;
96
96
  await this.subscribe(this.onChange);
97
97
  }
@@ -145,12 +145,15 @@ export class WebSocketProgramAccountSubscriber<T>
145
145
  }
146
146
  }
147
147
 
148
- unsubscribe(): Promise<void> {
148
+ unsubscribe(onResub = false): Promise<void> {
149
+ if (!onResub) {
150
+ this.resubTimeoutMs = undefined;
151
+ }
149
152
  this.isUnsubscribing = true;
150
153
  clearTimeout(this.timeoutId);
151
154
  this.timeoutId = undefined;
152
155
 
153
- if (this.listenerId) {
156
+ if (this.listenerId != null) {
154
157
  const promise = this.program.provider.connection
155
158
  .removeAccountChangeListener(this.listenerId)
156
159
  .then(() => {
@@ -1181,6 +1181,19 @@ export class AdminClient extends DriftClient {
1181
1181
  });
1182
1182
  }
1183
1183
 
1184
+ public async updatePhoenixFulfillmentConfigStatus(
1185
+ phoenixFulfillmentConfig: PublicKey,
1186
+ status: SpotFulfillmentConfigStatus
1187
+ ): Promise<TransactionSignature> {
1188
+ return await this.program.rpc.phoenixFulfillmentConfigStatus(status, {
1189
+ accounts: {
1190
+ admin: this.wallet.publicKey,
1191
+ state: await this.getStatePublicKey(),
1192
+ phoenixFulfillmentConfig,
1193
+ },
1194
+ });
1195
+ }
1196
+
1184
1197
  public async updateSpotMarketExpiry(
1185
1198
  spotMarketIndex: number,
1186
1199
  expiryTs: BN
@@ -99,3 +99,7 @@ export const OPEN_ORDER_MARGIN_REQUIREMENT = QUOTE_PRECISION.div(new BN(100));
99
99
  export const DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT = new BN(
100
100
  -25
101
101
  ).mul(QUOTE_PRECISION);
102
+
103
+ export const ACCOUNT_AGE_DELETION_CUTOFF_SECONDS = 60 * 60 * 24 * 13; // 13 days
104
+ export const IDLE_TIME_SLOTS = 9000;
105
+ export const SLOT_TIME_ESTIMATE_MS = 400;