@drift-labs/sdk 2.52.0-beta.1 → 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 (81) hide show
  1. package/VERSION +1 -1
  2. package/examples/phoenix.ts +11 -4
  3. package/lib/accounts/webSocketAccountSubscriber.d.ts +1 -1
  4. package/lib/accounts/webSocketAccountSubscriber.js +7 -4
  5. package/lib/accounts/webSocketProgramAccountSubscriber.d.ts +1 -1
  6. package/lib/accounts/webSocketProgramAccountSubscriber.js +7 -4
  7. package/lib/dlob/orderBookLevels.d.ts +22 -0
  8. package/lib/dlob/orderBookLevels.js +115 -1
  9. package/lib/driftClient.d.ts +3 -1
  10. package/lib/driftClient.js +30 -8
  11. package/lib/events/webSocketLogProvider.js +3 -3
  12. package/lib/factory/bigNum.d.ts +1 -1
  13. package/lib/factory/bigNum.js +5 -2
  14. package/lib/idl/drift.json +52 -1
  15. package/lib/index.d.ts +1 -1
  16. package/lib/index.js +1 -1
  17. package/lib/math/amm.d.ts +5 -1
  18. package/lib/math/amm.js +62 -13
  19. package/lib/math/state.js +3 -0
  20. package/lib/orderSubscriber/OrderSubscriber.js +1 -0
  21. package/lib/orderSubscriber/WebsocketSubscription.d.ts +4 -1
  22. package/lib/orderSubscriber/WebsocketSubscription.js +25 -1
  23. package/lib/orderSubscriber/types.d.ts +1 -0
  24. package/lib/phoenix/phoenixSubscriber.js +10 -6
  25. package/lib/priorityFee/averageOverSlotsStrategy.d.ts +12 -0
  26. package/lib/priorityFee/averageOverSlotsStrategy.js +28 -0
  27. package/lib/priorityFee/averageStrategy.d.ts +7 -0
  28. package/lib/priorityFee/averageStrategy.js +11 -0
  29. package/lib/priorityFee/ewmaStrategy.d.ts +13 -0
  30. package/lib/priorityFee/ewmaStrategy.js +33 -0
  31. package/lib/priorityFee/index.d.ts +7 -0
  32. package/lib/priorityFee/index.js +23 -0
  33. package/lib/priorityFee/maxOverSlotsStrategy.d.ts +12 -0
  34. package/lib/priorityFee/maxOverSlotsStrategy.js +29 -0
  35. package/lib/priorityFee/maxStrategy.d.ts +7 -0
  36. package/lib/priorityFee/maxStrategy.js +9 -0
  37. package/lib/priorityFee/priorityFeeSubscriber.d.ts +15 -4
  38. package/lib/priorityFee/priorityFeeSubscriber.js +38 -14
  39. package/lib/priorityFee/types.d.ts +6 -0
  40. package/lib/priorityFee/types.js +2 -0
  41. package/lib/slot/SlotSubscriber.d.ts +11 -3
  42. package/lib/slot/SlotSubscriber.js +40 -4
  43. package/lib/types.d.ts +4 -1
  44. package/lib/types.js +1 -0
  45. package/lib/user.d.ts +1 -1
  46. package/lib/user.js +15 -8
  47. package/lib/userMap/userMap.d.ts +3 -0
  48. package/lib/userMap/userMap.js +9 -0
  49. package/package.json +1 -1
  50. package/src/accounts/webSocketAccountSubscriber.ts +7 -4
  51. package/src/accounts/webSocketProgramAccountSubscriber.ts +7 -4
  52. package/src/dlob/orderBookLevels.ts +136 -0
  53. package/src/driftClient.ts +46 -8
  54. package/src/events/webSocketLogProvider.ts +3 -3
  55. package/src/factory/bigNum.ts +11 -2
  56. package/src/idl/drift.json +52 -1
  57. package/src/index.ts +1 -1
  58. package/src/math/amm.ts +159 -25
  59. package/src/math/state.ts +3 -0
  60. package/src/orderSubscriber/OrderSubscriber.ts +1 -0
  61. package/src/orderSubscriber/WebsocketSubscription.ts +28 -0
  62. package/src/orderSubscriber/types.ts +1 -0
  63. package/src/phoenix/phoenixSubscriber.ts +14 -8
  64. package/src/priorityFee/averageOverSlotsStrategy.ts +30 -0
  65. package/src/priorityFee/averageStrategy.ts +11 -0
  66. package/src/priorityFee/ewmaStrategy.ts +40 -0
  67. package/src/priorityFee/index.ts +7 -0
  68. package/src/priorityFee/maxOverSlotsStrategy.ts +31 -0
  69. package/src/priorityFee/maxStrategy.ts +7 -0
  70. package/src/priorityFee/priorityFeeSubscriber.ts +46 -19
  71. package/src/priorityFee/types.ts +5 -0
  72. package/src/slot/SlotSubscriber.ts +52 -5
  73. package/src/types.ts +2 -1
  74. package/src/user.ts +25 -8
  75. package/src/userMap/userMap.ts +12 -0
  76. package/tests/amm/test.ts +219 -11
  77. package/tests/bn/test.ts +27 -0
  78. package/tests/dlob/helpers.ts +1 -1
  79. package/tests/dlob/test.ts +372 -2
  80. package/tests/tx/priorityFeeStrategy.ts +97 -0
  81. package/tests/user/helpers.ts +1 -0
package/lib/math/amm.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.calculateMaxBaseAssetAmountFillable = exports.calculateQuoteAssetAmountSwapped = exports.calculateMaxBaseAssetAmountToTrade = exports.calculateTerminalPrice = exports.getSwapDirection = exports.calculateSwapOutput = exports.calculateSpreadReserves = exports.calculateSpread = exports.calculateSpreadBN = exports.calculateVolSpreadBN = exports.calculateMaxSpread = exports.calculateEffectiveLeverage = exports.calculateInventoryScale = exports.calculateMarketOpenBidAsk = exports.calculateAmmReservesAfterSwap = exports.calculatePrice = exports.calculateBidAskPrice = exports.calculateUpdatedAMMSpreadReserves = exports.calculateUpdatedAMM = exports.calculateNewAmm = exports.calculateOptimalPegAndBudget = exports.calculatePegFromTargetPrice = void 0;
3
+ exports.calculateMaxBaseAssetAmountFillable = exports.calculateQuoteAssetAmountSwapped = exports.calculateMaxBaseAssetAmountToTrade = exports.calculateTerminalPrice = exports.getSwapDirection = exports.calculateSwapOutput = exports.calculateSpreadReserves = exports.calculateSpread = exports.calculateSpreadBN = exports.calculateVolSpreadBN = exports.calculateMaxSpread = exports.calculateEffectiveLeverage = exports.calculateReferencePriceOffset = exports.calculateInventoryScale = exports.calculateInventoryLiquidityRatio = exports.calculateMarketOpenBidAsk = exports.calculateAmmReservesAfterSwap = exports.calculatePrice = exports.calculateBidAskPrice = exports.calculateUpdatedAMMSpreadReserves = exports.calculateUpdatedAMM = exports.calculateNewAmm = exports.calculateOptimalPegAndBudget = exports.calculatePegFromTargetPrice = void 0;
4
4
  const anchor_1 = require("@coral-xyz/anchor");
5
5
  const numericConstants_1 = require("../constants/numericConstants");
6
6
  const types_1 = require("../types");
@@ -201,11 +201,7 @@ function calculateMarketOpenBidAsk(baseAssetReserve, minBaseAssetReserve, maxBas
201
201
  return [openBids, openAsks];
202
202
  }
203
203
  exports.calculateMarketOpenBidAsk = calculateMarketOpenBidAsk;
204
- function calculateInventoryScale(baseAssetAmountWithAmm, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve, directionalSpread, maxSpread) {
205
- if (baseAssetAmountWithAmm.eq(numericConstants_1.ZERO)) {
206
- return 1;
207
- }
208
- const MAX_BID_ASK_INVENTORY_SKEW_FACTOR = numericConstants_1.BID_ASK_SPREAD_PRECISION.mul(new anchor_1.BN(10));
204
+ function calculateInventoryLiquidityRatio(baseAssetAmountWithAmm, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve) {
209
205
  // inventory skew
210
206
  const [openBids, openAsks] = calculateMarketOpenBidAsk(baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve);
211
207
  const minSideLiquidity = anchor_1.BN.min(openBids.abs(), openAsks.abs());
@@ -213,6 +209,15 @@ function calculateInventoryScale(baseAssetAmountWithAmm, baseAssetReserve, minBa
213
209
  .mul(numericConstants_1.PERCENTAGE_PRECISION)
214
210
  .div(anchor_1.BN.max(minSideLiquidity, numericConstants_1.ONE))
215
211
  .abs(), numericConstants_1.PERCENTAGE_PRECISION);
212
+ return inventoryScaleBN;
213
+ }
214
+ exports.calculateInventoryLiquidityRatio = calculateInventoryLiquidityRatio;
215
+ function calculateInventoryScale(baseAssetAmountWithAmm, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve, directionalSpread, maxSpread) {
216
+ if (baseAssetAmountWithAmm.eq(numericConstants_1.ZERO)) {
217
+ return 1;
218
+ }
219
+ const MAX_BID_ASK_INVENTORY_SKEW_FACTOR = numericConstants_1.BID_ASK_SPREAD_PRECISION.mul(new anchor_1.BN(10));
220
+ const inventoryScaleBN = calculateInventoryLiquidityRatio(baseAssetAmountWithAmm, baseAssetReserve, minBaseAssetReserve, maxBaseAssetReserve);
216
221
  const inventoryScaleMaxBN = anchor_1.BN.max(MAX_BID_ASK_INVENTORY_SKEW_FACTOR, new anchor_1.BN(maxSpread)
217
222
  .mul(numericConstants_1.BID_ASK_SPREAD_PRECISION)
218
223
  .div(new anchor_1.BN(Math.max(directionalSpread, 1))));
@@ -220,6 +225,36 @@ function calculateInventoryScale(baseAssetAmountWithAmm, baseAssetReserve, minBa
220
225
  return inventoryScaleCapped;
221
226
  }
222
227
  exports.calculateInventoryScale = calculateInventoryScale;
228
+ function calculateReferencePriceOffset(reservePrice, last24hAvgFundingRate, liquidityFraction, oracleTwapFast, markTwapFast, oracleTwapSlow, markTwapSlow, maxOffsetPct) {
229
+ if (last24hAvgFundingRate.eq(numericConstants_1.ZERO)) {
230
+ return numericConstants_1.ZERO;
231
+ }
232
+ const maxOffsetInPrice = new anchor_1.BN(maxOffsetPct)
233
+ .mul(reservePrice)
234
+ .div(numericConstants_1.PERCENTAGE_PRECISION);
235
+ // Calculate quote denominated market premium
236
+ const markPremiumMinute = (0, __1.clampBN)(markTwapFast.sub(oracleTwapFast), maxOffsetInPrice.mul(new anchor_1.BN(-1)), maxOffsetInPrice);
237
+ const markPremiumHour = (0, __1.clampBN)(markTwapSlow.sub(oracleTwapSlow), maxOffsetInPrice.mul(new anchor_1.BN(-1)), maxOffsetInPrice);
238
+ // Convert last24hAvgFundingRate to quote denominated premium
239
+ const markPremiumDay = (0, __1.clampBN)(last24hAvgFundingRate.div(numericConstants_1.FUNDING_RATE_BUFFER_PRECISION).mul(new anchor_1.BN(24)), maxOffsetInPrice.mul(new anchor_1.BN(-1)), maxOffsetInPrice);
240
+ // Take average clamped premium as the price-based offset
241
+ const markPremiumAvg = markPremiumMinute
242
+ .add(markPremiumHour)
243
+ .add(markPremiumDay)
244
+ .div(new anchor_1.BN(3));
245
+ const markPremiumAvgPct = markPremiumAvg
246
+ .mul(numericConstants_1.PRICE_PRECISION)
247
+ .div(reservePrice);
248
+ const inventoryPct = (0, __1.clampBN)(liquidityFraction.mul(new anchor_1.BN(maxOffsetPct)).div(numericConstants_1.PERCENTAGE_PRECISION), maxOffsetInPrice.mul(new anchor_1.BN(-1)), maxOffsetInPrice);
249
+ // Only apply when inventory is consistent with recent and 24h market premium
250
+ let offsetPct = markPremiumAvgPct.add(inventoryPct);
251
+ if (!(0, __1.sigNum)(inventoryPct).eq((0, __1.sigNum)(markPremiumAvgPct))) {
252
+ offsetPct = numericConstants_1.ZERO;
253
+ }
254
+ const clampedOffsetPct = (0, __1.clampBN)(offsetPct, new anchor_1.BN(-maxOffsetPct), new anchor_1.BN(maxOffsetPct));
255
+ return clampedOffsetPct;
256
+ }
257
+ exports.calculateReferencePriceOffset = calculateReferencePriceOffset;
223
258
  function calculateEffectiveLeverage(baseSpread, quoteAssetReserve, terminalQuoteAssetReserve, pegMultiplier, netBaseAssetAmount, reservePrice, totalFeeMinusDistributions) {
224
259
  // vAMM skew
225
260
  const netBaseAssetValue = quoteAssetReserve
@@ -283,6 +318,8 @@ function calculateSpreadBN(baseSpread, lastOracleReservePriceSpreadPct, lastOrac
283
318
  halfRevenueRetreatAmount: 0,
284
319
  longSpreadwRevRetreat: 0,
285
320
  shortSpreadwRevRetreat: 0,
321
+ longSpreadwOffsetShrink: 0,
322
+ shortSpreadwOffsetShrink: 0,
286
323
  totalSpread: 0,
287
324
  longSpread: 0,
288
325
  shortSpread: 0,
@@ -380,11 +417,13 @@ function calculateSpreadBN(baseSpread, lastOracleReservePriceSpreadPct, lastOrac
380
417
  return [longSpread, shortSpread];
381
418
  }
382
419
  exports.calculateSpreadBN = calculateSpreadBN;
383
- function calculateSpread(amm, oraclePriceData, now) {
420
+ function calculateSpread(amm, oraclePriceData, now, reservePrice) {
384
421
  if (amm.baseSpread == 0 || amm.curveUpdateIntensity == 0) {
385
422
  return [amm.baseSpread / 2, amm.baseSpread / 2];
386
423
  }
387
- const reservePrice = calculatePrice(amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier);
424
+ if (!reservePrice) {
425
+ reservePrice = calculatePrice(amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier);
426
+ }
388
427
  const targetPrice = (oraclePriceData === null || oraclePriceData === void 0 ? void 0 : oraclePriceData.price) || reservePrice;
389
428
  const confInterval = oraclePriceData.confidence || numericConstants_1.ZERO;
390
429
  const targetMarkSpreadPct = reservePrice
@@ -410,10 +449,15 @@ function calculateSpreadReserves(amm, oraclePriceData, now) {
410
449
  quoteAssetReserve: amm.quoteAssetReserve,
411
450
  };
412
451
  }
413
- const spreadFraction = anchor_1.BN.max(new anchor_1.BN(spread / 2), numericConstants_1.ONE);
452
+ let spreadFraction = new anchor_1.BN(spread / 2);
453
+ // make non-zero
454
+ if (spreadFraction.eq(numericConstants_1.ZERO)) {
455
+ spreadFraction = spread >= 0 ? new anchor_1.BN(1) : new anchor_1.BN(-1);
456
+ }
414
457
  const quoteAssetReserveDelta = amm.quoteAssetReserve.div(numericConstants_1.BID_ASK_SPREAD_PRECISION.div(spreadFraction));
415
458
  let quoteAssetReserve;
416
- if ((0, types_1.isVariant)(direction, 'long')) {
459
+ if ((spread >= 0 && (0, types_1.isVariant)(direction, 'long')) ||
460
+ (spread <= 0 && (0, types_1.isVariant)(direction, 'short'))) {
417
461
  quoteAssetReserve = amm.quoteAssetReserve.add(quoteAssetReserveDelta);
418
462
  }
419
463
  else {
@@ -425,9 +469,14 @@ function calculateSpreadReserves(amm, oraclePriceData, now) {
425
469
  quoteAssetReserve,
426
470
  };
427
471
  }
428
- const [longSpread, shortSpread] = calculateSpread(amm, oraclePriceData, now);
429
- const askReserves = calculateSpreadReserve(longSpread, types_1.PositionDirection.LONG, amm);
430
- const bidReserves = calculateSpreadReserve(shortSpread, types_1.PositionDirection.SHORT, amm);
472
+ const reservePrice = calculatePrice(amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier);
473
+ // always allow 10 bps of price offset, up to a fifth of the market's max_spread
474
+ const maxOffset = Math.max(amm.maxSpread / 5, numericConstants_1.PERCENTAGE_PRECISION.toNumber() / 1000);
475
+ const liquidityFraction = calculateInventoryLiquidityRatio(amm.baseAssetAmountWithAmm, amm.baseAssetReserve, amm.minBaseAssetReserve, amm.maxBaseAssetReserve);
476
+ const referencePriceOffset = calculateReferencePriceOffset(reservePrice, amm.last24HAvgFundingRate, liquidityFraction, amm.historicalOracleData.lastOraclePriceTwap5Min, amm.lastMarkPriceTwap5Min, amm.historicalOracleData.lastOraclePriceTwap, amm.lastMarkPriceTwap, maxOffset);
477
+ const [longSpread, shortSpread] = calculateSpread(amm, oraclePriceData, now, reservePrice);
478
+ const askReserves = calculateSpreadReserve(longSpread + referencePriceOffset.toNumber(), types_1.PositionDirection.LONG, amm);
479
+ const bidReserves = calculateSpreadReserve(shortSpread + referencePriceOffset.toNumber(), types_1.PositionDirection.SHORT, amm);
431
480
  return [bidReserves, askReserves];
432
481
  }
433
482
  exports.calculateSpreadReserves = calculateSpreadReserves;
package/lib/math/state.js CHANGED
@@ -22,6 +22,9 @@ function calculateInitUserFee(stateAccount) {
22
22
  }
23
23
  exports.calculateInitUserFee = calculateInitUserFee;
24
24
  function getMaxNumberOfSubAccounts(stateAccount) {
25
+ if (stateAccount.maxNumberOfSubAccounts <= 5) {
26
+ return new __1.BN(stateAccount.maxNumberOfSubAccounts);
27
+ }
25
28
  return new __1.BN(stateAccount.maxNumberOfSubAccounts).muln(100);
26
29
  }
27
30
  exports.getMaxNumberOfSubAccounts = getMaxNumberOfSubAccounts;
@@ -28,6 +28,7 @@ class OrderSubscriber {
28
28
  commitment: this.commitment,
29
29
  skipInitialLoad: config.subscriptionConfig.skipInitialLoad,
30
30
  resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
31
+ resyncIntervalMs: config.subscriptionConfig.resyncIntervalMs,
31
32
  });
32
33
  }
33
34
  if ((_a = config.fastDecode) !== null && _a !== void 0 ? _a : true) {
@@ -5,12 +5,15 @@ export declare class WebsocketSubscription {
5
5
  private commitment;
6
6
  private skipInitialLoad;
7
7
  private resubTimeoutMs?;
8
+ private resyncIntervalMs?;
8
9
  private subscriber?;
9
- constructor({ orderSubscriber, commitment, skipInitialLoad, resubTimeoutMs, }: {
10
+ private resyncTimeoutId?;
11
+ constructor({ orderSubscriber, commitment, skipInitialLoad, resubTimeoutMs, resyncIntervalMs, }: {
10
12
  orderSubscriber: OrderSubscriber;
11
13
  commitment: Commitment;
12
14
  skipInitialLoad?: boolean;
13
15
  resubTimeoutMs?: number;
16
+ resyncIntervalMs?: number;
14
17
  });
15
18
  subscribe(): Promise<void>;
16
19
  unsubscribe(): Promise<void>;
@@ -4,11 +4,12 @@ exports.WebsocketSubscription = void 0;
4
4
  const memcmp_1 = require("../memcmp");
5
5
  const webSocketProgramAccountSubscriber_1 = require("../accounts/webSocketProgramAccountSubscriber");
6
6
  class WebsocketSubscription {
7
- constructor({ orderSubscriber, commitment, skipInitialLoad = false, resubTimeoutMs, }) {
7
+ constructor({ orderSubscriber, commitment, skipInitialLoad = false, resubTimeoutMs, resyncIntervalMs, }) {
8
8
  this.orderSubscriber = orderSubscriber;
9
9
  this.commitment = commitment;
10
10
  this.skipInitialLoad = skipInitialLoad;
11
11
  this.resubTimeoutMs = resubTimeoutMs;
12
+ this.resyncIntervalMs = resyncIntervalMs;
12
13
  }
13
14
  async subscribe() {
14
15
  if (this.subscriber) {
@@ -25,12 +26,35 @@ class WebsocketSubscription {
25
26
  if (!this.skipInitialLoad) {
26
27
  await this.orderSubscriber.fetch();
27
28
  }
29
+ if (this.resyncIntervalMs) {
30
+ const recursiveResync = () => {
31
+ this.resyncTimeoutId = setTimeout(() => {
32
+ this.orderSubscriber
33
+ .fetch()
34
+ .catch((e) => {
35
+ console.error('Failed to resync in OrderSubscriber');
36
+ console.log(e);
37
+ })
38
+ .finally(() => {
39
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
40
+ if (!this.resyncTimeoutId)
41
+ return;
42
+ recursiveResync();
43
+ });
44
+ }, this.resyncIntervalMs);
45
+ };
46
+ recursiveResync();
47
+ }
28
48
  }
29
49
  async unsubscribe() {
30
50
  if (!this.subscriber)
31
51
  return;
32
52
  await this.subscriber.unsubscribe();
33
53
  this.subscriber = undefined;
54
+ if (this.resyncTimeoutId !== undefined) {
55
+ clearTimeout(this.resyncTimeoutId);
56
+ this.resyncTimeoutId = undefined;
57
+ }
34
58
  }
35
59
  }
36
60
  exports.WebsocketSubscription = WebsocketSubscription;
@@ -11,6 +11,7 @@ export type OrderSubscriberConfig = {
11
11
  type: 'websocket';
12
12
  skipInitialLoad?: boolean;
13
13
  resubTimeoutMs?: number;
14
+ resyncIntervalMs?: number;
14
15
  commitment?: Commitment;
15
16
  };
16
17
  fastDecode?: boolean;
@@ -99,14 +99,18 @@ class PhoenixSubscriber {
99
99
  return this.getL2Levels('asks');
100
100
  }
101
101
  *getL2Levels(side) {
102
- const basePrecision = Math.pow(10, this.market.data.header.baseParams.decimals);
103
- const pricePrecision = numericConstants_1.PRICE_PRECISION.toNumber();
104
- const ladder = (0, phoenix_sdk_1.getMarketUiLadder)(this.market, this.lastSlot, this.lastUnixTimestamp, 20);
102
+ const tickSize = this.market.data.header
103
+ .tickSizeInQuoteAtomsPerBaseUnit;
104
+ const baseLotsToRawBaseUnits = this.market.baseLotsToRawBaseUnits(1);
105
+ const basePrecision = new anchor_1.BN(Math.pow(10, this.market.data.header.baseParams.decimals) *
106
+ baseLotsToRawBaseUnits);
107
+ const pricePrecision = numericConstants_1.PRICE_PRECISION.div(tickSize);
108
+ const ladder = (0, phoenix_sdk_1.getMarketLadder)(this.market, this.lastSlot, this.lastUnixTimestamp, 20);
105
109
  for (let i = 0; i < ladder[side].length; i++) {
106
- const { price, quantity } = ladder[side][i];
107
- const size = new anchor_1.BN(Math.floor(quantity * basePrecision));
110
+ const { priceInTicks, sizeInBaseLots } = ladder[side][i];
111
+ const size = sizeInBaseLots.mul(basePrecision);
108
112
  yield {
109
- price: new anchor_1.BN(Math.floor(price * pricePrecision)),
113
+ price: priceInTicks.mul(new anchor_1.BN(pricePrecision)),
110
114
  size,
111
115
  sources: {
112
116
  phoenix: size,
@@ -0,0 +1,12 @@
1
+ import { PriorityFeeStrategy } from './types';
2
+ export declare class AverageOverSlotsStrategy implements PriorityFeeStrategy {
3
+ private lookbackSlots;
4
+ /**
5
+ * @param lookbackSlots The number of slots to look back from the max slot in the sample
6
+ */
7
+ constructor(lookbackSlots?: number);
8
+ calculate(samples: {
9
+ slot: number;
10
+ prioritizationFee: number;
11
+ }[]): number;
12
+ }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AverageOverSlotsStrategy = void 0;
4
+ class AverageOverSlotsStrategy {
5
+ /**
6
+ * @param lookbackSlots The number of slots to look back from the max slot in the sample
7
+ */
8
+ constructor(lookbackSlots = 10) {
9
+ this.lookbackSlots = lookbackSlots;
10
+ }
11
+ calculate(samples) {
12
+ if (samples.length === 0) {
13
+ return 0;
14
+ }
15
+ const stopSlot = samples[0].slot - this.lookbackSlots;
16
+ let runningSumFees = 0;
17
+ let countFees = 0;
18
+ for (let i = 0; i < samples.length; i++) {
19
+ if (samples[i].slot <= stopSlot) {
20
+ return runningSumFees / countFees;
21
+ }
22
+ runningSumFees += samples[i].prioritizationFee;
23
+ countFees++;
24
+ }
25
+ return runningSumFees / countFees;
26
+ }
27
+ }
28
+ exports.AverageOverSlotsStrategy = AverageOverSlotsStrategy;
@@ -0,0 +1,7 @@
1
+ import { PriorityFeeStrategy } from './types';
2
+ export declare class AverageStrategy implements PriorityFeeStrategy {
3
+ calculate(samples: {
4
+ slot: number;
5
+ prioritizationFee: number;
6
+ }[]): number;
7
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AverageStrategy = void 0;
4
+ class AverageStrategy {
5
+ calculate(samples) {
6
+ return (samples.reduce((a, b) => {
7
+ return a + b.prioritizationFee;
8
+ }, 0) / samples.length);
9
+ }
10
+ }
11
+ exports.AverageStrategy = AverageStrategy;
@@ -0,0 +1,13 @@
1
+ import { PriorityFeeStrategy } from './types';
2
+ declare class EwmaStrategy implements PriorityFeeStrategy {
3
+ private halfLife;
4
+ /**
5
+ * @param halfLife The half life of the EWMA in slots. Default is 25 slots, approx 10 seconds.
6
+ */
7
+ constructor(halfLife?: number);
8
+ calculate(samples: {
9
+ slot: number;
10
+ prioritizationFee: number;
11
+ }[]): number;
12
+ }
13
+ export { EwmaStrategy };
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EwmaStrategy = void 0;
4
+ class EwmaStrategy {
5
+ /**
6
+ * @param halfLife The half life of the EWMA in slots. Default is 25 slots, approx 10 seconds.
7
+ */
8
+ constructor(halfLife = 25) {
9
+ this.halfLife = halfLife;
10
+ }
11
+ // samples provided in desc slot order
12
+ calculate(samples) {
13
+ if (samples.length === 0) {
14
+ return 0;
15
+ }
16
+ if (samples.length === 1) {
17
+ return samples[0].prioritizationFee;
18
+ }
19
+ let ewma = 0;
20
+ const samplesReversed = samples.slice().reverse();
21
+ for (let i = 0; i < samplesReversed.length; i++) {
22
+ if (i === 0) {
23
+ ewma = samplesReversed[i].prioritizationFee;
24
+ continue;
25
+ }
26
+ const gap = samplesReversed[i].slot - samplesReversed[i - 1].slot;
27
+ const alpha = 1 - Math.exp((Math.log(0.5) / this.halfLife) * gap);
28
+ ewma = alpha * samplesReversed[i].prioritizationFee + (1 - alpha) * ewma;
29
+ }
30
+ return ewma;
31
+ }
32
+ }
33
+ exports.EwmaStrategy = EwmaStrategy;
@@ -0,0 +1,7 @@
1
+ export * from './averageOverSlotsStrategy';
2
+ export * from './averageStrategy';
3
+ export * from './ewmaStrategy';
4
+ export * from './maxOverSlotsStrategy';
5
+ export * from './maxStrategy';
6
+ export * from './priorityFeeSubscriber';
7
+ export * from './types';
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./averageOverSlotsStrategy"), exports);
18
+ __exportStar(require("./averageStrategy"), exports);
19
+ __exportStar(require("./ewmaStrategy"), exports);
20
+ __exportStar(require("./maxOverSlotsStrategy"), exports);
21
+ __exportStar(require("./maxStrategy"), exports);
22
+ __exportStar(require("./priorityFeeSubscriber"), exports);
23
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,12 @@
1
+ import { PriorityFeeStrategy } from './types';
2
+ export declare class MaxOverSlotsStrategy implements PriorityFeeStrategy {
3
+ private lookbackSlots;
4
+ /**
5
+ * @param lookbackSlots The number of slots to look back from the max slot in the sample
6
+ */
7
+ constructor(lookbackSlots?: number);
8
+ calculate(samples: {
9
+ slot: number;
10
+ prioritizationFee: number;
11
+ }[]): number;
12
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MaxOverSlotsStrategy = void 0;
4
+ class MaxOverSlotsStrategy {
5
+ /**
6
+ * @param lookbackSlots The number of slots to look back from the max slot in the sample
7
+ */
8
+ constructor(lookbackSlots = 10) {
9
+ this.lookbackSlots = lookbackSlots;
10
+ }
11
+ calculate(samples) {
12
+ if (samples.length === 0) {
13
+ return 0;
14
+ }
15
+ // Assuming samples are sorted in descending order of slot.
16
+ const stopSlot = samples[0].slot - this.lookbackSlots;
17
+ let currMaxFee = samples[0].prioritizationFee;
18
+ for (let i = 0; i < samples.length; i++) {
19
+ if (samples[i].slot <= stopSlot) {
20
+ return currMaxFee;
21
+ }
22
+ if (samples[i].prioritizationFee > currMaxFee) {
23
+ currMaxFee = samples[i].prioritizationFee;
24
+ }
25
+ }
26
+ return currMaxFee;
27
+ }
28
+ }
29
+ exports.MaxOverSlotsStrategy = MaxOverSlotsStrategy;
@@ -0,0 +1,7 @@
1
+ import { PriorityFeeStrategy } from './types';
2
+ export declare class MaxStrategy implements PriorityFeeStrategy {
3
+ calculate(samples: {
4
+ slot: number;
5
+ prioritizationFee: number;
6
+ }[]): number;
7
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MaxStrategy = void 0;
4
+ class MaxStrategy {
5
+ calculate(samples) {
6
+ return Math.max(...samples.map((result) => result.prioritizationFee));
7
+ }
8
+ }
9
+ exports.MaxStrategy = MaxStrategy;
@@ -1,20 +1,31 @@
1
1
  import { Connection, PublicKey } from '@solana/web3.js';
2
+ import { PriorityFeeStrategy } from './types';
3
+ import { AverageOverSlotsStrategy } from './averageOverSlotsStrategy';
4
+ import { MaxOverSlotsStrategy } from './maxOverSlotsStrategy';
2
5
  export declare class PriorityFeeSubscriber {
3
6
  connection: Connection;
4
7
  frequencyMs: number;
5
8
  addresses: PublicKey[];
6
- slotsToCheck: number;
9
+ customStrategy?: PriorityFeeStrategy;
10
+ averageStrategy: AverageOverSlotsStrategy;
11
+ maxStrategy: MaxOverSlotsStrategy;
7
12
  intervalId?: ReturnType<typeof setTimeout>;
8
13
  latestPriorityFee: number;
9
- avgPriorityFee: number;
10
- maxPriorityFee: number;
14
+ lastStrategyResult: number;
15
+ lastCustomStrategyResult: number;
16
+ lastAvgStrategyResult: number;
17
+ lastMaxStrategyResult: number;
11
18
  lastSlotSeen: number;
12
- constructor({ connection, frequencyMs, addresses, slotsToCheck, }: {
19
+ constructor({ connection, frequencyMs, addresses, customStrategy, slotsToCheck, }: {
13
20
  connection: Connection;
14
21
  frequencyMs: number;
15
22
  addresses: PublicKey[];
23
+ customStrategy?: PriorityFeeStrategy;
16
24
  slotsToCheck?: number;
17
25
  });
26
+ get avgPriorityFee(): number;
27
+ get maxPriorityFee(): number;
28
+ get customPriorityFee(): number;
18
29
  subscribe(): Promise<void>;
19
30
  load(): Promise<void>;
20
31
  unsubscribe(): Promise<void>;
@@ -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 {};