@drift-labs/sdk 2.82.0-beta.0 → 2.82.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 (85) hide show
  1. package/README.md +65 -47
  2. package/VERSION +1 -1
  3. package/lib/accounts/types.d.ts +4 -0
  4. package/lib/accounts/webSocketAccountSubscriber.d.ts +3 -3
  5. package/lib/accounts/webSocketAccountSubscriber.js +15 -8
  6. package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +3 -3
  7. package/lib/accounts/webSocketDriftClientAccountSubscriber.js +5 -5
  8. package/lib/accounts/webSocketInsuranceFundStakeAccountSubscriber.d.ts +2 -2
  9. package/lib/accounts/webSocketInsuranceFundStakeAccountSubscriber.js +5 -3
  10. package/lib/accounts/webSocketProgramAccountSubscriber.d.ts +3 -3
  11. package/lib/accounts/webSocketProgramAccountSubscriber.js +15 -9
  12. package/lib/accounts/webSocketUserAccountSubscriber.d.ts +3 -3
  13. package/lib/accounts/webSocketUserAccountSubscriber.js +3 -3
  14. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +3 -3
  15. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +3 -3
  16. package/lib/auctionSubscriber/auctionSubscriber.d.ts +2 -2
  17. package/lib/auctionSubscriber/auctionSubscriber.js +3 -3
  18. package/lib/auctionSubscriber/types.d.ts +1 -0
  19. package/lib/constants/perpMarkets.js +2 -2
  20. package/lib/driftClient.d.ts +12 -1
  21. package/lib/driftClient.js +59 -16
  22. package/lib/driftClientConfig.d.ts +1 -0
  23. package/lib/orderSubscriber/OrderSubscriber.js +6 -3
  24. package/lib/orderSubscriber/WebsocketSubscription.d.ts +4 -3
  25. package/lib/orderSubscriber/WebsocketSubscription.js +3 -3
  26. package/lib/orderSubscriber/types.d.ts +1 -0
  27. package/lib/priorityFee/driftPriorityFeeMethod.d.ts +13 -3
  28. package/lib/priorityFee/driftPriorityFeeMethod.js +2 -2
  29. package/lib/priorityFee/index.d.ts +2 -0
  30. package/lib/priorityFee/index.js +2 -0
  31. package/lib/priorityFee/priorityFeeSubscriber.d.ts +1 -4
  32. package/lib/priorityFee/priorityFeeSubscriber.js +5 -4
  33. package/lib/priorityFee/priorityFeeSubscriberMap.d.ts +48 -0
  34. package/lib/priorityFee/priorityFeeSubscriberMap.js +88 -0
  35. package/lib/priorityFee/types.d.ts +8 -3
  36. package/lib/priorityFee/types.js +2 -1
  37. package/lib/tx/baseTxSender.js +3 -2
  38. package/lib/tx/fastSingleTxSender.d.ts +2 -0
  39. package/lib/tx/fastSingleTxSender.js +10 -8
  40. package/lib/tx/types.d.ts +5 -0
  41. package/lib/tx/types.js +12 -1
  42. package/lib/tx/utils.js +5 -1
  43. package/lib/user.d.ts +0 -10
  44. package/lib/user.js +6 -29
  45. package/lib/userConfig.d.ts +1 -0
  46. package/lib/userMap/WebsocketSubscription.d.ts +4 -3
  47. package/lib/userMap/WebsocketSubscription.js +3 -3
  48. package/lib/userMap/userMap.js +4 -1
  49. package/lib/userMap/userMapConfig.d.ts +1 -0
  50. package/lib/userStats.js +6 -3
  51. package/lib/userStatsConfig.d.ts +1 -0
  52. package/package.json +3 -3
  53. package/src/accounts/types.ts +5 -0
  54. package/src/accounts/webSocketAccountSubscriber.ts +34 -22
  55. package/src/accounts/webSocketDriftClientAccountSubscriber.ts +7 -6
  56. package/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts +6 -4
  57. package/src/accounts/webSocketProgramAccountSubscriber.ts +32 -22
  58. package/src/accounts/webSocketUserAccountSubscriber.ts +5 -4
  59. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +5 -4
  60. package/src/auctionSubscriber/auctionSubscriber.ts +10 -4
  61. package/src/auctionSubscriber/types.ts +1 -0
  62. package/src/blockhashSubscriber/types.ts +4 -0
  63. package/src/constants/perpMarkets.ts +2 -2
  64. package/src/driftClient.ts +70 -12
  65. package/src/driftClientConfig.ts +1 -0
  66. package/src/orderSubscriber/OrderSubscriber.ts +4 -1
  67. package/src/orderSubscriber/WebsocketSubscription.ts +6 -5
  68. package/src/orderSubscriber/types.ts +1 -0
  69. package/src/priorityFee/driftPriorityFeeMethod.ts +16 -4
  70. package/src/priorityFee/index.ts +2 -0
  71. package/src/priorityFee/priorityFeeSubscriber.ts +7 -7
  72. package/src/priorityFee/priorityFeeSubscriberMap.ts +112 -0
  73. package/src/priorityFee/types.ts +16 -3
  74. package/src/tx/baseTxSender.ts +8 -4
  75. package/src/tx/fastSingleTxSender.ts +12 -9
  76. package/src/tx/types.ts +12 -0
  77. package/src/tx/utils.ts +5 -1
  78. package/src/user.ts +7 -32
  79. package/src/userConfig.ts +1 -0
  80. package/src/userMap/WebsocketSubscription.ts +6 -5
  81. package/src/userMap/userMap.ts +4 -1
  82. package/src/userMap/userMapConfig.ts +1 -0
  83. package/src/userStats.ts +4 -1
  84. package/src/userStatsConfig.ts +1 -0
  85. package/tests/dlob/helpers.ts +4 -0
@@ -4,6 +4,7 @@ import {
4
4
  NotSubscribedError,
5
5
  UserStatsAccountSubscriber,
6
6
  UserStatsAccountEvents,
7
+ ResubOpts,
7
8
  } from './types';
8
9
  import { Program } from '@coral-xyz/anchor';
9
10
  import StrictEventEmitter from 'strict-event-emitter-types';
@@ -16,7 +17,7 @@ export class WebSocketUserStatsAccountSubscriber
16
17
  implements UserStatsAccountSubscriber
17
18
  {
18
19
  isSubscribed: boolean;
19
- reconnectTimeoutMs?: number;
20
+ resubOpts?: ResubOpts;
20
21
  commitment?: Commitment;
21
22
  program: Program;
22
23
  eventEmitter: StrictEventEmitter<EventEmitter, UserStatsAccountEvents>;
@@ -27,14 +28,14 @@ export class WebSocketUserStatsAccountSubscriber
27
28
  public constructor(
28
29
  program: Program,
29
30
  userStatsAccountPublicKey: PublicKey,
30
- reconnectTimeoutMs?: number,
31
+ resubOpts?: ResubOpts,
31
32
  commitment?: Commitment
32
33
  ) {
33
34
  this.isSubscribed = false;
34
35
  this.program = program;
35
36
  this.userStatsAccountPublicKey = userStatsAccountPublicKey;
36
37
  this.eventEmitter = new EventEmitter();
37
- this.reconnectTimeoutMs = reconnectTimeoutMs;
38
+ this.resubOpts = resubOpts;
38
39
  this.commitment = commitment;
39
40
  }
40
41
 
@@ -48,7 +49,7 @@ export class WebSocketUserStatsAccountSubscriber
48
49
  this.program,
49
50
  this.userStatsAccountPublicKey,
50
51
  undefined,
51
- this.reconnectTimeoutMs,
52
+ this.resubOpts,
52
53
  this.commitment
53
54
  );
54
55
 
@@ -6,20 +6,26 @@ import { EventEmitter } from 'events';
6
6
  import { UserAccount } from '../types';
7
7
  import { ConfirmOptions, Context, PublicKey } from '@solana/web3.js';
8
8
  import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber';
9
+ import { ResubOpts } from '../accounts/types';
9
10
 
10
11
  export class AuctionSubscriber {
11
12
  private driftClient: DriftClient;
12
13
  private opts: ConfirmOptions;
13
- private resubTimeoutMs?: number;
14
+ private resubOpts?: ResubOpts;
14
15
 
15
16
  eventEmitter: StrictEventEmitter<EventEmitter, AuctionSubscriberEvents>;
16
17
  private subscriber: WebSocketProgramAccountSubscriber<UserAccount>;
17
18
 
18
- constructor({ driftClient, opts, resubTimeoutMs }: AuctionSubscriberConfig) {
19
+ constructor({
20
+ driftClient,
21
+ opts,
22
+ resubTimeoutMs,
23
+ logResubMessages,
24
+ }: AuctionSubscriberConfig) {
19
25
  this.driftClient = driftClient;
20
26
  this.opts = opts || this.driftClient.opts;
21
27
  this.eventEmitter = new EventEmitter();
22
- this.resubTimeoutMs = resubTimeoutMs;
28
+ this.resubOpts = { resubTimeoutMs, logResubMessages };
23
29
  }
24
30
 
25
31
  public async subscribe() {
@@ -35,7 +41,7 @@ export class AuctionSubscriber {
35
41
  filters: [getUserFilter(), getUserWithAuctionFilter()],
36
42
  commitment: this.opts.commitment,
37
43
  },
38
- this.resubTimeoutMs
44
+ this.resubOpts
39
45
  );
40
46
  }
41
47
 
@@ -6,6 +6,7 @@ export type AuctionSubscriberConfig = {
6
6
  driftClient: DriftClient;
7
7
  opts?: ConfirmOptions;
8
8
  resubTimeoutMs?: number;
9
+ logResubMessages?: boolean;
9
10
  };
10
11
 
11
12
  export interface AuctionSubscriberEvents {
@@ -1,8 +1,12 @@
1
1
  import { Commitment, Connection } from '@solana/web3.js';
2
2
 
3
3
  export type BlockhashSubscriberConfig = {
4
+ /// rpcUrl to poll block hashes from, one of rpcUrl or Connection must provided
4
5
  rpcUrl?: string;
6
+ /// connection to poll block hashes from, one of rpcUrl or Connection must provided
5
7
  connection?: Connection;
8
+ /// commitment to poll block hashes with, default is 'confirmed'
6
9
  commitment?: Commitment;
10
+ /// interval to poll block hashes, default is 1000 ms
7
11
  updateIntervalMs?: number;
8
12
  };
@@ -553,9 +553,9 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [
553
553
  symbol: 'KMNO-PERP',
554
554
  baseAssetSymbol: 'KMNO',
555
555
  marketIndex: 28,
556
- oracle: new PublicKey('sDAQaZQJQ4RXAxH3x526mbEXyQZT15ktkL84d7hmk7M'),
556
+ oracle: new PublicKey('6ynsvjkE2UoiRScbDx7ZxbBsyn7wyvg5P1vENvhtkG1C'),
557
557
  launchTs: 1712240681000,
558
- oracleSource: OracleSource.Prelaunch,
558
+ oracleSource: OracleSource.SWITCHBOARD,
559
559
  },
560
560
  {
561
561
  fullName: 'Tensor',
@@ -251,11 +251,13 @@ export class DriftClient {
251
251
  this.userAccountSubscriptionConfig = {
252
252
  type: 'websocket',
253
253
  resubTimeoutMs: config.accountSubscription?.resubTimeoutMs,
254
+ logResubMessages: config.accountSubscription?.logResubMessages,
254
255
  commitment: config.accountSubscription?.commitment,
255
256
  };
256
257
  this.userStatsAccountSubscriptionConfig = {
257
258
  type: 'websocket',
258
259
  resubTimeoutMs: config.accountSubscription?.resubTimeoutMs,
260
+ logResubMessages: config.accountSubscription?.logResubMessages,
259
261
  commitment: config.accountSubscription?.commitment,
260
262
  };
261
263
  }
@@ -298,7 +300,10 @@ export class DriftClient {
298
300
  config.spotMarketIndexes ?? [],
299
301
  config.oracleInfos ?? [],
300
302
  noMarketsAndOraclesSpecified,
301
- config.accountSubscription?.resubTimeoutMs,
303
+ {
304
+ resubTimeoutMs: config.accountSubscription?.resubTimeoutMs,
305
+ logResubMessages: config.accountSubscription?.logResubMessages,
306
+ },
302
307
  config.accountSubscription?.commitment
303
308
  );
304
309
  }
@@ -6488,18 +6493,33 @@ export class DriftClient {
6488
6493
  }
6489
6494
 
6490
6495
  public async settleRevenueToInsuranceFund(
6491
- marketIndex: number
6496
+ spotMarketIndex: number,
6497
+ subAccountId?: number,
6498
+ txParams?: TxParams
6492
6499
  ): Promise<TransactionSignature> {
6493
- const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
6500
+ const tx = await this.buildTransaction(
6501
+ await this.getSettleRevenueToInsuranceFundIx(
6502
+ spotMarketIndex,
6503
+ subAccountId
6504
+ ),
6505
+ txParams
6506
+ );
6507
+ const { txSig } = await this.sendTransaction(tx, [], this.opts);
6508
+ return txSig;
6509
+ }
6494
6510
 
6511
+ public async getSettleRevenueToInsuranceFundIx(
6512
+ spotMarketIndex: number,
6513
+ subAccountId?: number
6514
+ ): Promise<TransactionInstruction> {
6515
+ const spotMarketAccount = this.getSpotMarketAccount(spotMarketIndex);
6495
6516
  const remainingAccounts = this.getRemainingAccounts({
6496
- userAccounts: [this.getUserAccount()],
6517
+ userAccounts: [this.getUserAccount(subAccountId)],
6497
6518
  useMarketLastSlotCache: true,
6498
- writableSpotMarketIndexes: [marketIndex],
6519
+ writableSpotMarketIndexes: [spotMarketIndex],
6499
6520
  });
6500
-
6501
6521
  const ix = await this.program.instruction.settleRevenueToInsuranceFund(
6502
- marketIndex,
6522
+ spotMarketIndex,
6503
6523
  {
6504
6524
  accounts: {
6505
6525
  state: await this.getStatePublicKey(),
@@ -6512,11 +6532,7 @@ export class DriftClient {
6512
6532
  remainingAccounts,
6513
6533
  }
6514
6534
  );
6515
-
6516
- const tx = await this.buildTransaction(ix);
6517
-
6518
- const { txSig } = await this.sendTransaction(tx, [], this.opts);
6519
- return txSig;
6535
+ return ix;
6520
6536
  }
6521
6537
 
6522
6538
  public async resolvePerpPnlDeficit(
@@ -6629,6 +6645,48 @@ export class DriftClient {
6629
6645
  return extendedInfo;
6630
6646
  }
6631
6647
 
6648
+ /**
6649
+ * Calculates taker / maker fee (as a percentage, e.g. .001 = 10 basis points) for particular marketType
6650
+ * @param marketType
6651
+ * @param positionMarketIndex
6652
+ * @returns : {takerFee: number, makerFee: number} Precision None
6653
+ */
6654
+ public getMarketFees(
6655
+ marketType: MarketType,
6656
+ marketIndex?: number,
6657
+ user?: User
6658
+ ) {
6659
+ let feeTier;
6660
+ if (user) {
6661
+ feeTier = user.getUserFeeTier(marketType);
6662
+ } else {
6663
+ const state = this.getStateAccount();
6664
+ feeTier = isVariant(marketType, 'perp')
6665
+ ? state.perpFeeStructure.feeTiers[0]
6666
+ : state.spotFeeStructure.feeTiers[0];
6667
+ }
6668
+
6669
+ let takerFee = feeTier.feeNumerator / feeTier.feeDenominator;
6670
+ let makerFee =
6671
+ feeTier.makerRebateNumerator / feeTier.makerRebateDenominator;
6672
+
6673
+ if (marketIndex !== undefined) {
6674
+ let marketAccount = null;
6675
+ if (isVariant(marketType, 'perp')) {
6676
+ marketAccount = this.getPerpMarketAccount(marketIndex);
6677
+ } else {
6678
+ marketAccount = this.getSpotMarketAccount(marketIndex);
6679
+ }
6680
+ takerFee += (takerFee * marketAccount.feeAdjustment) / 100;
6681
+ makerFee += (makerFee * marketAccount.feeAdjustment) / 100;
6682
+ }
6683
+
6684
+ return {
6685
+ takerFee,
6686
+ makerFee,
6687
+ };
6688
+ }
6689
+
6632
6690
  /**
6633
6691
  * Returns the market index and type for a given market name
6634
6692
  * E.g. "SOL-PERP" -> { marketIndex: 0, marketType: MarketType.PERP }
@@ -39,6 +39,7 @@ export type DriftClientSubscriptionConfig =
39
39
  | {
40
40
  type: 'websocket';
41
41
  resubTimeoutMs?: number;
42
+ logResubMessages?: boolean;
42
43
  commitment?: Commitment;
43
44
  }
44
45
  | {
@@ -39,7 +39,10 @@ export class OrderSubscriber {
39
39
  orderSubscriber: this,
40
40
  commitment: this.commitment,
41
41
  skipInitialLoad: config.subscriptionConfig.skipInitialLoad,
42
- resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
42
+ resubOpts: {
43
+ resubTimeoutMs: config.subscriptionConfig?.resubTimeoutMs,
44
+ logResubMessages: config.subscriptionConfig?.logResubMessages,
45
+ },
43
46
  resyncIntervalMs: config.subscriptionConfig.resyncIntervalMs,
44
47
  decoded: config.decodeData,
45
48
  });
@@ -3,12 +3,13 @@ import { getNonIdleUserFilter, getUserFilter } from '../memcmp';
3
3
  import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber';
4
4
  import { UserAccount } from '../types';
5
5
  import { Commitment, Context, PublicKey } from '@solana/web3.js';
6
+ import { ResubOpts } from '../accounts/types';
6
7
 
7
8
  export class WebsocketSubscription {
8
9
  private orderSubscriber: OrderSubscriber;
9
10
  private commitment: Commitment;
10
11
  private skipInitialLoad: boolean;
11
- private resubTimeoutMs?: number;
12
+ private resubOpts?: ResubOpts;
12
13
  private resyncIntervalMs?: number;
13
14
 
14
15
  private subscriber?: WebSocketProgramAccountSubscriber<UserAccount>;
@@ -20,21 +21,21 @@ export class WebsocketSubscription {
20
21
  orderSubscriber,
21
22
  commitment,
22
23
  skipInitialLoad = false,
23
- resubTimeoutMs,
24
+ resubOpts,
24
25
  resyncIntervalMs,
25
26
  decoded = true,
26
27
  }: {
27
28
  orderSubscriber: OrderSubscriber;
28
29
  commitment: Commitment;
29
30
  skipInitialLoad?: boolean;
30
- resubTimeoutMs?: number;
31
+ resubOpts?: ResubOpts;
31
32
  resyncIntervalMs?: number;
32
33
  decoded?: boolean;
33
34
  }) {
34
35
  this.orderSubscriber = orderSubscriber;
35
36
  this.commitment = commitment;
36
37
  this.skipInitialLoad = skipInitialLoad;
37
- this.resubTimeoutMs = resubTimeoutMs;
38
+ this.resubOpts = resubOpts;
38
39
  this.resyncIntervalMs = resyncIntervalMs;
39
40
  this.decoded = decoded;
40
41
  }
@@ -53,7 +54,7 @@ export class WebsocketSubscription {
53
54
  filters: [getUserFilter(), getNonIdleUserFilter()],
54
55
  commitment: this.commitment,
55
56
  },
56
- this.resubTimeoutMs
57
+ this.resubOpts
57
58
  );
58
59
 
59
60
  await this.subscriber.subscribe(
@@ -14,6 +14,7 @@ export type OrderSubscriberConfig = {
14
14
  type: 'websocket';
15
15
  skipInitialLoad?: boolean;
16
16
  resubTimeoutMs?: number;
17
+ logResubMessages?: boolean;
17
18
  resyncIntervalMs?: number;
18
19
  commitment?: Commitment;
19
20
  };
@@ -1,18 +1,30 @@
1
1
  import fetch from 'node-fetch';
2
- import { HeliusPriorityFeeLevels } from './heliusPriorityFeeMethod';
2
+ import { HeliusPriorityLevel } from './heliusPriorityFeeMethod';
3
3
 
4
- export type DriftPriorityFeeResponse = HeliusPriorityFeeLevels[];
4
+ export type DriftMarketInfo = {
5
+ marketType: string;
6
+ marketIndex: number;
7
+ };
8
+
9
+ export type DriftPriorityFeeLevels = {
10
+ [key in HeliusPriorityLevel]: number;
11
+ } & {
12
+ marketType: 'perp' | 'spot';
13
+ marketIndex: number;
14
+ };
15
+
16
+ export type DriftPriorityFeeResponse = DriftPriorityFeeLevels[];
5
17
 
6
18
  export async function fetchDriftPriorityFee(
7
19
  url: string,
8
20
  marketTypes: string[],
9
- marketIndexs: number[]
21
+ marketIndexes: number[]
10
22
  ): Promise<DriftPriorityFeeResponse> {
11
23
  try {
12
24
  const response = await fetch(
13
25
  `${url}/batchPriorityFees?marketType=${marketTypes.join(
14
26
  ','
15
- )}&marketIndex=${marketIndexs.join(',')}`
27
+ )}&marketIndex=${marketIndexes.join(',')}`
16
28
  );
17
29
  if (!response.ok) {
18
30
  throw new Error(`HTTP error! status: ${response.status}`);
@@ -4,6 +4,8 @@ export * from './ewmaStrategy';
4
4
  export * from './maxOverSlotsStrategy';
5
5
  export * from './maxStrategy';
6
6
  export * from './priorityFeeSubscriber';
7
+ export * from './priorityFeeSubscriberMap';
7
8
  export * from './solanaPriorityFeeMethod';
8
9
  export * from './heliusPriorityFeeMethod';
10
+ export * from './driftPriorityFeeMethod';
9
11
  export * from './types';
@@ -1,5 +1,6 @@
1
1
  import { Connection, PublicKey } from '@solana/web3.js';
2
2
  import {
3
+ DEFAULT_PRIORITY_FEE_MAP_FREQUENCY_MS,
3
4
  PriorityFeeMethod,
4
5
  PriorityFeeStrategy,
5
6
  PriorityFeeSubscriberConfig,
@@ -12,12 +13,10 @@ import {
12
13
  HeliusPriorityLevel,
13
14
  fetchHeliusPriorityFee,
14
15
  } from './heliusPriorityFeeMethod';
15
- import { fetchDriftPriorityFee } from './driftPriorityFeeMethod';
16
-
17
- export type DriftMarketInfo = {
18
- marketType: string;
19
- marketIndex: number;
20
- };
16
+ import {
17
+ fetchDriftPriorityFee,
18
+ DriftMarketInfo,
19
+ } from './driftPriorityFeeMethod';
21
20
 
22
21
  export class PriorityFeeSubscriber {
23
22
  connection: Connection;
@@ -46,7 +45,8 @@ export class PriorityFeeSubscriber {
46
45
 
47
46
  public constructor(config: PriorityFeeSubscriberConfig) {
48
47
  this.connection = config.connection;
49
- this.frequencyMs = config.frequencyMs;
48
+ this.frequencyMs =
49
+ config.frequencyMs ?? DEFAULT_PRIORITY_FEE_MAP_FREQUENCY_MS;
50
50
  this.addresses = config.addresses
51
51
  ? config.addresses.map((address) => address.toBase58())
52
52
  : [];
@@ -0,0 +1,112 @@
1
+ import {
2
+ DriftMarketInfo,
3
+ DriftPriorityFeeLevels,
4
+ DriftPriorityFeeResponse,
5
+ fetchDriftPriorityFee,
6
+ } from './driftPriorityFeeMethod';
7
+ import {
8
+ DEFAULT_PRIORITY_FEE_MAP_FREQUENCY_MS,
9
+ PriorityFeeSubscriberMapConfig,
10
+ } from './types';
11
+
12
+ /**
13
+ * takes advantage of /batchPriorityFees endpoint from drift hosted priority fee service
14
+ */
15
+ export class PriorityFeeSubscriberMap {
16
+ frequencyMs: number;
17
+ intervalId?: ReturnType<typeof setTimeout>;
18
+
19
+ driftMarkets?: DriftMarketInfo[];
20
+ driftPriorityFeeEndpoint?: string;
21
+ feesMap: Map<string, Map<number, DriftPriorityFeeLevels>>; // marketType -> marketIndex -> priority fee
22
+
23
+ public constructor(config: PriorityFeeSubscriberMapConfig) {
24
+ this.frequencyMs = config.frequencyMs;
25
+ this.frequencyMs =
26
+ config.frequencyMs ?? DEFAULT_PRIORITY_FEE_MAP_FREQUENCY_MS;
27
+ this.driftPriorityFeeEndpoint = config.driftPriorityFeeEndpoint;
28
+ this.driftMarkets = config.driftMarkets;
29
+ this.feesMap = new Map<string, Map<number, DriftPriorityFeeLevels>>();
30
+ this.feesMap.set('perp', new Map<number, DriftPriorityFeeLevels>());
31
+ this.feesMap.set('spot', new Map<number, DriftPriorityFeeLevels>());
32
+ }
33
+
34
+ private updateFeesMap(driftPriorityFeeResponse: DriftPriorityFeeResponse) {
35
+ driftPriorityFeeResponse.forEach((fee: DriftPriorityFeeLevels) => {
36
+ this.feesMap.get(fee.marketType)!.set(fee.marketIndex, fee);
37
+ });
38
+ }
39
+
40
+ public async subscribe(): Promise<void> {
41
+ if (this.intervalId) {
42
+ return;
43
+ }
44
+
45
+ await this.load();
46
+ this.intervalId = setInterval(this.load.bind(this), this.frequencyMs);
47
+ }
48
+
49
+ public async unsubscribe(): Promise<void> {
50
+ if (this.intervalId) {
51
+ clearInterval(this.intervalId);
52
+ this.intervalId = undefined;
53
+ }
54
+ }
55
+
56
+ public async load(): Promise<void> {
57
+ try {
58
+ if (!this.driftMarkets) {
59
+ return;
60
+ }
61
+ const fees = await fetchDriftPriorityFee(
62
+ this.driftPriorityFeeEndpoint!,
63
+ this.driftMarkets.map((m) => m.marketType),
64
+ this.driftMarkets.map((m) => m.marketIndex)
65
+ );
66
+ this.updateFeesMap(fees);
67
+ } catch (e) {
68
+ console.error('Error fetching drift priority fees', e);
69
+ }
70
+ }
71
+
72
+ public updateMarketTypeAndIndex(driftMarkets: DriftMarketInfo[]) {
73
+ this.driftMarkets = driftMarkets;
74
+ }
75
+
76
+ public getPriorityFees(
77
+ marketType: string,
78
+ marketIndex: number
79
+ ): DriftPriorityFeeLevels | undefined {
80
+ return this.feesMap.get(marketType)?.get(marketIndex);
81
+ }
82
+ }
83
+
84
+ /** Example usage:
85
+ async function main() {
86
+ const driftMarkets: DriftMarketInfo[] = [
87
+ { marketType: 'perp', marketIndex: 0 },
88
+ { marketType: 'perp', marketIndex: 1 },
89
+ { marketType: 'spot', marketIndex: 2 }
90
+ ];
91
+
92
+ const subscriber = new PriorityFeeSubscriberMap({
93
+ driftPriorityFeeEndpoint: 'https://dlob.drift.trade',
94
+ frequencyMs: 5000,
95
+ driftMarkets
96
+ });
97
+ await subscriber.subscribe();
98
+
99
+ for (let i = 0; i < 20; i++) {
100
+ await new Promise(resolve => setTimeout(resolve, 1000));
101
+ driftMarkets.forEach(market => {
102
+ const fees = subscriber.getPriorityFees(market.marketType, market.marketIndex);
103
+ console.log(`Priority fees for ${market.marketType} market ${market.marketIndex}:`, fees);
104
+ });
105
+ }
106
+
107
+
108
+ await subscriber.unsubscribe();
109
+ }
110
+
111
+ main().catch(console.error);
112
+ */
@@ -1,8 +1,12 @@
1
1
  import { Connection, PublicKey } from '@solana/web3.js';
2
2
  import { SolanaPriorityFeeResponse } from './solanaPriorityFeeMethod';
3
3
  import { HeliusPriorityFeeResponse } from './heliusPriorityFeeMethod';
4
- import { DriftMarketInfo } from './priorityFeeSubscriber';
5
- import { DriftPriorityFeeResponse } from './driftPriorityFeeMethod';
4
+ import {
5
+ DriftMarketInfo,
6
+ DriftPriorityFeeResponse,
7
+ } from './driftPriorityFeeMethod';
8
+
9
+ export const DEFAULT_PRIORITY_FEE_MAP_FREQUENCY_MS = 10_000;
6
10
 
7
11
  export interface PriorityFeeStrategy {
8
12
  // calculate the priority fee for a given set of samples.
@@ -25,7 +29,7 @@ export type PriorityFeeSubscriberConfig = {
25
29
  /// rpc connection, optional if using priorityFeeMethod.HELIUS
26
30
  connection?: Connection;
27
31
  /// frequency to make RPC calls to update priority fee samples, in milliseconds
28
- frequencyMs: number;
32
+ frequencyMs?: number;
29
33
  /// addresses you plan to write lock, used to determine priority fees
30
34
  addresses?: PublicKey[];
31
35
  /// drift market type and index, optionally provide at initialization time if using priorityFeeMethod.DRIFT
@@ -45,3 +49,12 @@ export type PriorityFeeSubscriberConfig = {
45
49
  /// multiplier applied to priority fee before maxFeeMicroLamports, defaults to 1.0
46
50
  priorityFeeMultiplier?: number;
47
51
  };
52
+
53
+ export type PriorityFeeSubscriberMapConfig = {
54
+ /// frequency to make RPC calls to update priority fee samples, in milliseconds
55
+ frequencyMs?: number;
56
+ /// drift market type and associated market index to query
57
+ driftMarkets?: DriftMarketInfo[];
58
+ /// url for drift cached priority fee endpoint
59
+ driftPriorityFeeEndpoint: string;
60
+ };
@@ -2,6 +2,7 @@ import {
2
2
  ConfirmationStrategy,
3
3
  ExtraConfirmationOptions,
4
4
  TxSender,
5
+ TxSendError,
5
6
  TxSigAndSlot,
6
7
  } from './types';
7
8
  import {
@@ -25,6 +26,7 @@ import bs58 from 'bs58';
25
26
  import { IWallet } from '../types';
26
27
 
27
28
  const DEFAULT_TIMEOUT = 35000;
29
+ const NOT_CONFIRMED_ERROR_CODE = -1001;
28
30
 
29
31
  export abstract class BaseTxSender implements TxSender {
30
32
  connection: Connection;
@@ -282,10 +284,11 @@ export abstract class BaseTxSender implements TxSender {
282
284
  }
283
285
  this.timeoutCount += 1;
284
286
  const duration = (Date.now() - start) / 1000;
285
- throw new Error(
287
+ throw new TxSendError(
286
288
  `Transaction was not confirmed in ${duration.toFixed(
287
289
  2
288
- )} seconds. It is unknown if it succeeded or failed. Check signature ${signature} using the Solana Explorer or CLI tools.`
290
+ )} seconds. It is unknown if it succeeded or failed. Check signature ${signature} using the Solana Explorer or CLI tools.`,
291
+ NOT_CONFIRMED_ERROR_CODE
289
292
  );
290
293
  }
291
294
 
@@ -317,10 +320,11 @@ export abstract class BaseTxSender implements TxSender {
317
320
  // Transaction not confirmed within 30 seconds
318
321
  this.timeoutCount += 1;
319
322
  const duration = (Date.now() - start) / 1000;
320
- throw new Error(
323
+ throw new TxSendError(
321
324
  `Transaction was not confirmed in ${duration.toFixed(
322
325
  2
323
- )} seconds. It is unknown if it succeeded or failed. Check signature ${signature} using the Solana Explorer or CLI tools.`
326
+ )} seconds. It is unknown if it succeeded or failed. Check signature ${signature} using the Solana Explorer or CLI tools.`,
327
+ NOT_CONFIRMED_ERROR_CODE
324
328
  );
325
329
  }
326
330
 
@@ -29,6 +29,7 @@ export class FastSingleTxSender extends BaseTxSender {
29
29
  recentBlockhash: string;
30
30
  skipConfirmation: boolean;
31
31
  blockhashCommitment: Commitment;
32
+ blockhashIntervalId: NodeJS.Timer;
32
33
 
33
34
  public constructor({
34
35
  connection,
@@ -71,15 +72,17 @@ export class FastSingleTxSender extends BaseTxSender {
71
72
  }
72
73
 
73
74
  startBlockhashRefreshLoop(): void {
74
- setInterval(async () => {
75
- try {
76
- this.recentBlockhash = (
77
- await this.connection.getLatestBlockhash(this.blockhashCommitment)
78
- ).blockhash;
79
- } catch (e) {
80
- console.error('Error in startBlockhashRefreshLoop: ', e);
81
- }
82
- }, this.blockhashRefreshInterval);
75
+ if (this.blockhashRefreshInterval > 0) {
76
+ this.blockhashIntervalId = setInterval(async () => {
77
+ try {
78
+ this.recentBlockhash = (
79
+ await this.connection.getLatestBlockhash(this.blockhashCommitment)
80
+ ).blockhash;
81
+ } catch (e) {
82
+ console.error('Error in startBlockhashRefreshLoop: ', e);
83
+ }
84
+ }, this.blockhashRefreshInterval);
85
+ }
83
86
  }
84
87
 
85
88
  async prepareTx(
package/src/tx/types.ts CHANGED
@@ -60,3 +60,15 @@ export interface TxSender {
60
60
 
61
61
  getTimeoutCount(): number;
62
62
  }
63
+
64
+ export class TxSendError extends Error {
65
+ constructor(
66
+ public message: string,
67
+ public code: number
68
+ ) {
69
+ super(message);
70
+ if (Error.captureStackTrace) {
71
+ Error.captureStackTrace(this, TxSendError);
72
+ }
73
+ }
74
+ }
package/src/tx/utils.ts CHANGED
@@ -53,7 +53,11 @@ export async function getSignedTransactionMap(
53
53
  });
54
54
 
55
55
  const signedTxs = await wallet.signAllTransactions(
56
- txsToSign.filter((tx) => tx !== undefined)
56
+ txsToSign
57
+ .map((tx) => {
58
+ return tx as Transaction;
59
+ })
60
+ .filter((tx) => tx !== undefined)
57
61
  );
58
62
 
59
63
  signedTxs.forEach((signedTx, index) => {