@drift-labs/sdk 2.156.0-beta.1 → 2.156.0-beta.3

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.156.0-beta.1
1
+ 2.156.0-beta.3
@@ -33,6 +33,7 @@ export * from './accounts/types';
33
33
  export * from './addresses/pda';
34
34
  export * from './adminClient';
35
35
  export * from './assert/assert';
36
+ export { PythLazerSubscriber, PythLazerPriceFeedArray } from './pyth';
36
37
  export * from './testClient';
37
38
  export * from './user';
38
39
  export * from './userConfig';
@@ -17,7 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.pyth = exports.PublicKey = exports.BN = exports.CustomizedCadenceBulkAccountLoader = exports.WebSocketDriftClientAccountSubscriberV2 = exports.WebSocketProgramAccountsSubscriberV2 = exports.WebSocketProgramUserAccountSubscriber = exports.WebSocketProgramAccountSubscriber = exports.WebSocketAccountSubscriberV2 = void 0;
20
+ exports.pyth = exports.PublicKey = exports.BN = exports.PythLazerSubscriber = exports.CustomizedCadenceBulkAccountLoader = exports.WebSocketDriftClientAccountSubscriberV2 = exports.WebSocketProgramAccountsSubscriberV2 = exports.WebSocketProgramUserAccountSubscriber = exports.WebSocketProgramAccountSubscriber = exports.WebSocketAccountSubscriberV2 = void 0;
21
21
  const anchor_1 = require("@coral-xyz/anchor");
22
22
  Object.defineProperty(exports, "BN", { enumerable: true, get: function () { return anchor_1.BN; } });
23
23
  const web3_js_1 = require("@solana/web3.js");
@@ -62,6 +62,8 @@ __exportStar(require("./accounts/types"), exports);
62
62
  __exportStar(require("./addresses/pda"), exports);
63
63
  __exportStar(require("./adminClient"), exports);
64
64
  __exportStar(require("./assert/assert"), exports);
65
+ var pyth_1 = require("./pyth");
66
+ Object.defineProperty(exports, "PythLazerSubscriber", { enumerable: true, get: function () { return pyth_1.PythLazerSubscriber; } });
65
67
  __exportStar(require("./testClient"), exports);
66
68
  __exportStar(require("./user"), exports);
67
69
  __exportStar(require("./userConfig"), exports);
@@ -1,3 +1,4 @@
1
1
  export { WormholeCoreBridgeSolana, WORMHOLE_CORE_BRIDGE_SOLANA_IDL, PythSolanaReceiver, PriceUpdateAccount, } from './types';
2
2
  export { DEFAULT_WORMHOLE_PROGRAM_ID, DEFAULT_RECEIVER_PROGRAM_ID, } from './constants';
3
3
  export { getGuardianSetPda } from './utils';
4
+ export { PythLazerSubscriber, PythLazerPriceFeedArray, } from './pythLazerSubscriber';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getGuardianSetPda = exports.DEFAULT_RECEIVER_PROGRAM_ID = exports.DEFAULT_WORMHOLE_PROGRAM_ID = exports.WORMHOLE_CORE_BRIDGE_SOLANA_IDL = void 0;
3
+ exports.PythLazerSubscriber = exports.getGuardianSetPda = exports.DEFAULT_RECEIVER_PROGRAM_ID = exports.DEFAULT_WORMHOLE_PROGRAM_ID = exports.WORMHOLE_CORE_BRIDGE_SOLANA_IDL = void 0;
4
4
  var types_1 = require("./types");
5
5
  Object.defineProperty(exports, "WORMHOLE_CORE_BRIDGE_SOLANA_IDL", { enumerable: true, get: function () { return types_1.WORMHOLE_CORE_BRIDGE_SOLANA_IDL; } });
6
6
  var constants_1 = require("./constants");
@@ -8,3 +8,5 @@ Object.defineProperty(exports, "DEFAULT_WORMHOLE_PROGRAM_ID", { enumerable: true
8
8
  Object.defineProperty(exports, "DEFAULT_RECEIVER_PROGRAM_ID", { enumerable: true, get: function () { return constants_1.DEFAULT_RECEIVER_PROGRAM_ID; } });
9
9
  var utils_1 = require("./utils");
10
10
  Object.defineProperty(exports, "getGuardianSetPda", { enumerable: true, get: function () { return utils_1.getGuardianSetPda; } });
11
+ var pythLazerSubscriber_1 = require("./pythLazerSubscriber");
12
+ Object.defineProperty(exports, "PythLazerSubscriber", { enumerable: true, get: function () { return pythLazerSubscriber_1.PythLazerSubscriber; } });
@@ -0,0 +1,89 @@
1
+ /// <reference types="node" />
2
+ import { Channel } from '@pythnetwork/pyth-lazer-sdk';
3
+ import { DriftEnv } from '../config';
4
+ /**
5
+ * Configuration for a group of Pyth Lazer price feeds.
6
+ */
7
+ export type PythLazerPriceFeedArray = {
8
+ /** Optional channel for update frequency (e.g., 'fixed_rate@200ms') */
9
+ channel?: Channel;
10
+ /** Array of Pyth Lazer price feed IDs to subscribe to */
11
+ priceFeedIds: number[];
12
+ };
13
+ /**
14
+ * Manages subscriptions to Pyth Lazer price feeds and provides access to real-time price data.
15
+ * Automatically filters out non-stable feeds and handles reconnection logic.
16
+ */
17
+ export declare class PythLazerSubscriber {
18
+ private endpoints;
19
+ private token;
20
+ private priceFeedArrays;
21
+ private resubTimeoutMs;
22
+ private sdkLogging;
23
+ private static readonly SYMBOLS_API_URL;
24
+ private symbolsCache;
25
+ private pythLazerClient?;
26
+ feedIdChunkToPriceMessage: Map<string, string>;
27
+ feedIdToPrice: Map<number, number>;
28
+ feedIdHashToFeedIds: Map<string, number[]>;
29
+ subscriptionIdsToFeedIdsHash: Map<number, string>;
30
+ allSubscribedIds: number[];
31
+ timeoutId?: NodeJS.Timeout;
32
+ receivingData: boolean;
33
+ isUnsubscribing: boolean;
34
+ marketIndextoPriceFeedIdChunk: Map<number, number[]>;
35
+ marketIndextoPriceFeedId: Map<number, number>;
36
+ /**
37
+ * Creates a new PythLazerSubscriber instance.
38
+ * @param endpoints - Array of WebSocket endpoint URLs for Pyth Lazer
39
+ * @param token - Authentication token for Pyth Lazer API
40
+ * @param priceFeedArrays - Array of price feed configurations to subscribe to
41
+ * @param env - Drift environment (mainnet-beta, devnet, etc.)
42
+ * @param resubTimeoutMs - Milliseconds to wait before resubscribing on data timeout
43
+ * @param sdkLogging - Whether to log Pyth SDK logs to the console. This is very noisy but could be useful for debugging.
44
+ */
45
+ constructor(endpoints: string[], token: string, priceFeedArrays: PythLazerPriceFeedArray[], env?: DriftEnv, resubTimeoutMs?: number, sdkLogging?: boolean);
46
+ private fetchSymbolsIfNeeded;
47
+ private filterStableFeeds;
48
+ /**
49
+ * Subscribes to Pyth Lazer price feeds. Automatically filters out non-stable feeds
50
+ * and establishes WebSocket connections for real-time price updates.
51
+ */
52
+ subscribe(): Promise<void>;
53
+ protected setTimeout(): void;
54
+ /**
55
+ * Unsubscribes from all Pyth Lazer price feeds and shuts down WebSocket connections.
56
+ */
57
+ unsubscribe(): Promise<void>;
58
+ hash(arr: number[]): string;
59
+ /**
60
+ * Retrieves the latest Solana-format price message for a group of feed IDs.
61
+ * @param feedIds - Array of price feed IDs
62
+ * @returns Hex-encoded price message data, or undefined if not available
63
+ */
64
+ getLatestPriceMessage(feedIds: number[]): Promise<string | undefined>;
65
+ /**
66
+ * Retrieves the latest Solana-format price message for a specific market.
67
+ * @param marketIndex - The market index to get price data for
68
+ * @returns Hex-encoded price message data, or undefined if not found
69
+ */
70
+ getLatestPriceMessageForMarketIndex(marketIndex: number): Promise<string | undefined>;
71
+ /**
72
+ * Gets the array of price feed IDs associated with a market index.
73
+ * @param marketIndex - The market index to look up
74
+ * @returns Array of price feed IDs, or empty array if not found
75
+ */
76
+ getPriceFeedIdsFromMarketIndex(marketIndex: number): number[];
77
+ /**
78
+ * Gets the array of price feed IDs from a subscription hash.
79
+ * @param hash - The subscription hash
80
+ * @returns Array of price feed IDs, or empty array if not found
81
+ */
82
+ getPriceFeedIdsFromHash(hash: string): number[];
83
+ /**
84
+ * Gets the current parsed price for a specific market index.
85
+ * @param marketIndex - The market index to get the price for
86
+ * @returns The price as a number, or undefined if not available
87
+ */
88
+ getPriceFromMarketIndex(marketIndex: number): number | undefined;
89
+ }
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PythLazerSubscriber = void 0;
4
+ const pyth_lazer_sdk_1 = require("@pythnetwork/pyth-lazer-sdk");
5
+ const perpMarkets_1 = require("../constants/perpMarkets");
6
+ /**
7
+ * Manages subscriptions to Pyth Lazer price feeds and provides access to real-time price data.
8
+ * Automatically filters out non-stable feeds and handles reconnection logic.
9
+ */
10
+ class PythLazerSubscriber {
11
+ /**
12
+ * Creates a new PythLazerSubscriber instance.
13
+ * @param endpoints - Array of WebSocket endpoint URLs for Pyth Lazer
14
+ * @param token - Authentication token for Pyth Lazer API
15
+ * @param priceFeedArrays - Array of price feed configurations to subscribe to
16
+ * @param env - Drift environment (mainnet-beta, devnet, etc.)
17
+ * @param resubTimeoutMs - Milliseconds to wait before resubscribing on data timeout
18
+ * @param sdkLogging - Whether to log Pyth SDK logs to the console. This is very noisy but could be useful for debugging.
19
+ */
20
+ constructor(endpoints, token, priceFeedArrays, env = 'devnet', resubTimeoutMs = 2000, sdkLogging = false) {
21
+ this.endpoints = endpoints;
22
+ this.token = token;
23
+ this.priceFeedArrays = priceFeedArrays;
24
+ this.resubTimeoutMs = resubTimeoutMs;
25
+ this.sdkLogging = sdkLogging;
26
+ this.symbolsCache = null;
27
+ this.feedIdChunkToPriceMessage = new Map();
28
+ this.feedIdToPrice = new Map();
29
+ this.feedIdHashToFeedIds = new Map();
30
+ this.subscriptionIdsToFeedIdsHash = new Map();
31
+ this.allSubscribedIds = [];
32
+ this.receivingData = false;
33
+ this.isUnsubscribing = false;
34
+ this.marketIndextoPriceFeedIdChunk = new Map();
35
+ this.marketIndextoPriceFeedId = new Map();
36
+ const markets = perpMarkets_1.PerpMarkets[env].filter((market) => market.pythLazerId !== undefined);
37
+ this.allSubscribedIds = this.priceFeedArrays
38
+ .map((array) => array.priceFeedIds)
39
+ .flat();
40
+ for (const priceFeedIds of priceFeedArrays) {
41
+ const filteredMarkets = markets.filter((market) => priceFeedIds.priceFeedIds.includes(market.pythLazerId));
42
+ for (const market of filteredMarkets) {
43
+ this.marketIndextoPriceFeedIdChunk.set(market.marketIndex, priceFeedIds.priceFeedIds);
44
+ this.marketIndextoPriceFeedId.set(market.marketIndex, market.pythLazerId);
45
+ }
46
+ }
47
+ }
48
+ async fetchSymbolsIfNeeded() {
49
+ if (this.symbolsCache !== null)
50
+ return;
51
+ try {
52
+ const response = await fetch(PythLazerSubscriber.SYMBOLS_API_URL);
53
+ if (!response.ok)
54
+ throw new Error(`HTTP ${response.status}`);
55
+ const symbols = await response.json();
56
+ this.symbolsCache = new Map();
57
+ for (const symbol of symbols) {
58
+ this.symbolsCache.set(symbol.pyth_lazer_id, {
59
+ name: symbol.name,
60
+ state: symbol.state,
61
+ });
62
+ }
63
+ }
64
+ catch (error) {
65
+ console.warn(`Failed to fetch Pyth Lazer symbols, proceeding with all feeds: ${error}`);
66
+ this.symbolsCache = new Map(); // Empty map = no filtering
67
+ }
68
+ }
69
+ filterStableFeeds(feedIds) {
70
+ if (this.symbolsCache === null || this.symbolsCache.size === 0) {
71
+ return feedIds; // No filtering if cache unavailable
72
+ }
73
+ return feedIds.filter((feedId) => {
74
+ const info = this.symbolsCache.get(feedId);
75
+ if (!info) {
76
+ console.warn(`Feed ID ${feedId} not found in symbols API, including anyway`);
77
+ return true;
78
+ }
79
+ if (info.state !== 'stable') {
80
+ console.warn(`Removing feed ID ${feedId} (${info.name}) - state is "${info.state}", not "stable"`);
81
+ return false;
82
+ }
83
+ return true;
84
+ });
85
+ }
86
+ /**
87
+ * Subscribes to Pyth Lazer price feeds. Automatically filters out non-stable feeds
88
+ * and establishes WebSocket connections for real-time price updates.
89
+ */
90
+ async subscribe() {
91
+ var _a;
92
+ await this.fetchSymbolsIfNeeded();
93
+ this.pythLazerClient = await pyth_lazer_sdk_1.PythLazerClient.create({
94
+ token: this.token,
95
+ logger: this.sdkLogging ? console : undefined,
96
+ webSocketPoolConfig: {
97
+ urls: this.endpoints,
98
+ numConnections: 4, // Optionally specify number of parallel redundant connections to reduce the chance of dropped messages. The connections will round-robin across the provided URLs. Default is 4.
99
+ onError: (error) => {
100
+ console.error('⛔️ PythLazerClient error:', error.message);
101
+ },
102
+ onWebSocketError: (error) => {
103
+ console.error('⛔️ WebSocket error:', error.message);
104
+ },
105
+ onWebSocketPoolError: (error) => {
106
+ console.error('⛔️ WebSocket pool error:', error.message);
107
+ },
108
+ // Optional configuration for resilient WebSocket connections
109
+ rwsConfig: {
110
+ heartbeatTimeoutDurationMs: 5000, // Optional heartbeat timeout duration in milliseconds
111
+ maxRetryDelayMs: 1000, // Optional maximum retry delay in milliseconds
112
+ logAfterRetryCount: 10, // Optional log after how many retries
113
+ },
114
+ },
115
+ });
116
+ // Reset allSubscribedIds to rebuild with only stable feeds
117
+ this.allSubscribedIds = [];
118
+ let subscriptionId = 1;
119
+ for (const priceFeedArray of this.priceFeedArrays) {
120
+ const filteredFeedIds = this.filterStableFeeds(priceFeedArray.priceFeedIds);
121
+ if (filteredFeedIds.length === 0) {
122
+ console.warn(`All feeds filtered out for subscription ${subscriptionId}, skipping`);
123
+ continue;
124
+ }
125
+ // Update allSubscribedIds with only stable feeds
126
+ this.allSubscribedIds.push(...filteredFeedIds);
127
+ const feedIdsHash = this.hash(filteredFeedIds);
128
+ this.feedIdHashToFeedIds.set(feedIdsHash, filteredFeedIds);
129
+ this.subscriptionIdsToFeedIdsHash.set(subscriptionId, feedIdsHash);
130
+ // Update marketIndextoPriceFeedIdChunk to use filtered feeds
131
+ for (const [marketIndex, chunk,] of this.marketIndextoPriceFeedIdChunk.entries()) {
132
+ if (this.hash(chunk) === this.hash(priceFeedArray.priceFeedIds)) {
133
+ this.marketIndextoPriceFeedIdChunk.set(marketIndex, filteredFeedIds);
134
+ }
135
+ }
136
+ // Remove entries from marketIndextoPriceFeedId for filtered-out feeds
137
+ for (const [marketIndex, feedId,] of this.marketIndextoPriceFeedId.entries()) {
138
+ if (!filteredFeedIds.includes(feedId) &&
139
+ priceFeedArray.priceFeedIds.includes(feedId)) {
140
+ this.marketIndextoPriceFeedId.delete(marketIndex);
141
+ this.marketIndextoPriceFeedIdChunk.delete(marketIndex);
142
+ }
143
+ }
144
+ this.pythLazerClient.addMessageListener((message) => {
145
+ var _a, _b;
146
+ this.receivingData = true;
147
+ clearTimeout(this.timeoutId);
148
+ switch (message.type) {
149
+ case 'json': {
150
+ if (message.value.type == 'streamUpdated') {
151
+ if ((_a = message.value.solana) === null || _a === void 0 ? void 0 : _a.data) {
152
+ this.feedIdChunkToPriceMessage.set(this.subscriptionIdsToFeedIdsHash.get(message.value.subscriptionId), message.value.solana.data);
153
+ }
154
+ if ((_b = message.value.parsed) === null || _b === void 0 ? void 0 : _b.priceFeeds) {
155
+ for (const priceFeed of message.value.parsed.priceFeeds) {
156
+ const price = Number(priceFeed.price) *
157
+ Math.pow(10, Number(priceFeed.exponent));
158
+ this.feedIdToPrice.set(priceFeed.priceFeedId, price);
159
+ }
160
+ }
161
+ }
162
+ break;
163
+ }
164
+ default: {
165
+ break;
166
+ }
167
+ }
168
+ this.setTimeout();
169
+ });
170
+ this.pythLazerClient.send({
171
+ type: 'subscribe',
172
+ subscriptionId,
173
+ priceFeedIds: filteredFeedIds,
174
+ properties: ['price', 'bestAskPrice', 'bestBidPrice', 'exponent'],
175
+ formats: ['solana'],
176
+ deliveryFormat: 'json',
177
+ channel: (_a = priceFeedArray.channel) !== null && _a !== void 0 ? _a : 'fixed_rate@200ms',
178
+ jsonBinaryEncoding: 'hex',
179
+ });
180
+ subscriptionId++;
181
+ }
182
+ this.receivingData = true;
183
+ this.setTimeout();
184
+ }
185
+ setTimeout() {
186
+ this.timeoutId = setTimeout(async () => {
187
+ if (this.isUnsubscribing) {
188
+ // If we are in the process of unsubscribing, do not attempt to resubscribe
189
+ return;
190
+ }
191
+ if (this.receivingData) {
192
+ console.log(`No ws data from pyth lazer client resubscribing`);
193
+ await this.unsubscribe();
194
+ this.receivingData = false;
195
+ await this.subscribe();
196
+ }
197
+ }, this.resubTimeoutMs);
198
+ }
199
+ /**
200
+ * Unsubscribes from all Pyth Lazer price feeds and shuts down WebSocket connections.
201
+ */
202
+ async unsubscribe() {
203
+ var _a;
204
+ this.isUnsubscribing = true;
205
+ (_a = this.pythLazerClient) === null || _a === void 0 ? void 0 : _a.shutdown();
206
+ this.pythLazerClient = undefined;
207
+ clearTimeout(this.timeoutId);
208
+ this.timeoutId = undefined;
209
+ this.isUnsubscribing = false;
210
+ }
211
+ hash(arr) {
212
+ return 'h:' + arr.join('|');
213
+ }
214
+ /**
215
+ * Retrieves the latest Solana-format price message for a group of feed IDs.
216
+ * @param feedIds - Array of price feed IDs
217
+ * @returns Hex-encoded price message data, or undefined if not available
218
+ */
219
+ async getLatestPriceMessage(feedIds) {
220
+ return this.feedIdChunkToPriceMessage.get(this.hash(feedIds));
221
+ }
222
+ /**
223
+ * Retrieves the latest Solana-format price message for a specific market.
224
+ * @param marketIndex - The market index to get price data for
225
+ * @returns Hex-encoded price message data, or undefined if not found
226
+ */
227
+ async getLatestPriceMessageForMarketIndex(marketIndex) {
228
+ const feedIds = this.marketIndextoPriceFeedIdChunk.get(marketIndex);
229
+ if (!feedIds) {
230
+ return undefined;
231
+ }
232
+ return await this.getLatestPriceMessage(feedIds);
233
+ }
234
+ /**
235
+ * Gets the array of price feed IDs associated with a market index.
236
+ * @param marketIndex - The market index to look up
237
+ * @returns Array of price feed IDs, or empty array if not found
238
+ */
239
+ getPriceFeedIdsFromMarketIndex(marketIndex) {
240
+ return this.marketIndextoPriceFeedIdChunk.get(marketIndex) || [];
241
+ }
242
+ /**
243
+ * Gets the array of price feed IDs from a subscription hash.
244
+ * @param hash - The subscription hash
245
+ * @returns Array of price feed IDs, or empty array if not found
246
+ */
247
+ getPriceFeedIdsFromHash(hash) {
248
+ return this.feedIdHashToFeedIds.get(hash) || [];
249
+ }
250
+ /**
251
+ * Gets the current parsed price for a specific market index.
252
+ * @param marketIndex - The market index to get the price for
253
+ * @returns The price as a number, or undefined if not available
254
+ */
255
+ getPriceFromMarketIndex(marketIndex) {
256
+ const feedId = this.marketIndextoPriceFeedId.get(marketIndex);
257
+ if (feedId === undefined) {
258
+ return undefined;
259
+ }
260
+ return this.feedIdToPrice.get(feedId);
261
+ }
262
+ }
263
+ exports.PythLazerSubscriber = PythLazerSubscriber;
264
+ PythLazerSubscriber.SYMBOLS_API_URL = 'https://history.pyth-lazer.dourolabs.app/history/v1/symbols';
@@ -33,6 +33,7 @@ export * from './accounts/types';
33
33
  export * from './addresses/pda';
34
34
  export * from './adminClient';
35
35
  export * from './assert/assert';
36
+ export { PythLazerSubscriber, PythLazerPriceFeedArray } from './pyth';
36
37
  export * from './testClient';
37
38
  export * from './user';
38
39
  export * from './userConfig';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
package/lib/node/index.js CHANGED
@@ -17,7 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.pyth = exports.PublicKey = exports.BN = exports.CustomizedCadenceBulkAccountLoader = exports.WebSocketDriftClientAccountSubscriberV2 = exports.WebSocketProgramAccountsSubscriberV2 = exports.WebSocketProgramUserAccountSubscriber = exports.WebSocketProgramAccountSubscriber = exports.WebSocketAccountSubscriberV2 = void 0;
20
+ exports.pyth = exports.PublicKey = exports.BN = exports.PythLazerSubscriber = exports.CustomizedCadenceBulkAccountLoader = exports.WebSocketDriftClientAccountSubscriberV2 = exports.WebSocketProgramAccountsSubscriberV2 = exports.WebSocketProgramUserAccountSubscriber = exports.WebSocketProgramAccountSubscriber = exports.WebSocketAccountSubscriberV2 = void 0;
21
21
  const anchor_1 = require("@coral-xyz/anchor");
22
22
  Object.defineProperty(exports, "BN", { enumerable: true, get: function () { return anchor_1.BN; } });
23
23
  const web3_js_1 = require("@solana/web3.js");
@@ -62,6 +62,8 @@ __exportStar(require("./accounts/types"), exports);
62
62
  __exportStar(require("./addresses/pda"), exports);
63
63
  __exportStar(require("./adminClient"), exports);
64
64
  __exportStar(require("./assert/assert"), exports);
65
+ var pyth_1 = require("./pyth");
66
+ Object.defineProperty(exports, "PythLazerSubscriber", { enumerable: true, get: function () { return pyth_1.PythLazerSubscriber; } });
65
67
  __exportStar(require("./testClient"), exports);
66
68
  __exportStar(require("./user"), exports);
67
69
  __exportStar(require("./userConfig"), exports);
@@ -1,4 +1,5 @@
1
1
  export { WormholeCoreBridgeSolana, WORMHOLE_CORE_BRIDGE_SOLANA_IDL, PythSolanaReceiver, PriceUpdateAccount, } from './types';
2
2
  export { DEFAULT_WORMHOLE_PROGRAM_ID, DEFAULT_RECEIVER_PROGRAM_ID, } from './constants';
3
3
  export { getGuardianSetPda } from './utils';
4
+ export { PythLazerSubscriber, PythLazerPriceFeedArray, } from './pythLazerSubscriber';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pyth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,wBAAwB,EACxB,+BAA+B,EAC/B,kBAAkB,EAClB,kBAAkB,GAClB,MAAM,SAAS,CAAC;AACjB,OAAO,EACN,2BAA2B,EAC3B,2BAA2B,GAC3B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pyth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,wBAAwB,EACxB,+BAA+B,EAC/B,kBAAkB,EAClB,kBAAkB,GAClB,MAAM,SAAS,CAAC;AACjB,OAAO,EACN,2BAA2B,EAC3B,2BAA2B,GAC3B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EACN,mBAAmB,EACnB,uBAAuB,GACvB,MAAM,uBAAuB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getGuardianSetPda = exports.DEFAULT_RECEIVER_PROGRAM_ID = exports.DEFAULT_WORMHOLE_PROGRAM_ID = exports.WORMHOLE_CORE_BRIDGE_SOLANA_IDL = void 0;
3
+ exports.PythLazerSubscriber = exports.getGuardianSetPda = exports.DEFAULT_RECEIVER_PROGRAM_ID = exports.DEFAULT_WORMHOLE_PROGRAM_ID = exports.WORMHOLE_CORE_BRIDGE_SOLANA_IDL = void 0;
4
4
  var types_1 = require("./types");
5
5
  Object.defineProperty(exports, "WORMHOLE_CORE_BRIDGE_SOLANA_IDL", { enumerable: true, get: function () { return types_1.WORMHOLE_CORE_BRIDGE_SOLANA_IDL; } });
6
6
  var constants_1 = require("./constants");
@@ -8,3 +8,5 @@ Object.defineProperty(exports, "DEFAULT_WORMHOLE_PROGRAM_ID", { enumerable: true
8
8
  Object.defineProperty(exports, "DEFAULT_RECEIVER_PROGRAM_ID", { enumerable: true, get: function () { return constants_1.DEFAULT_RECEIVER_PROGRAM_ID; } });
9
9
  var utils_1 = require("./utils");
10
10
  Object.defineProperty(exports, "getGuardianSetPda", { enumerable: true, get: function () { return utils_1.getGuardianSetPda; } });
11
+ var pythLazerSubscriber_1 = require("./pythLazerSubscriber");
12
+ Object.defineProperty(exports, "PythLazerSubscriber", { enumerable: true, get: function () { return pythLazerSubscriber_1.PythLazerSubscriber; } });
@@ -0,0 +1,90 @@
1
+ /// <reference types="node" />
2
+ import { Channel } from '@pythnetwork/pyth-lazer-sdk';
3
+ import { DriftEnv } from '../config';
4
+ /**
5
+ * Configuration for a group of Pyth Lazer price feeds.
6
+ */
7
+ export type PythLazerPriceFeedArray = {
8
+ /** Optional channel for update frequency (e.g., 'fixed_rate@200ms') */
9
+ channel?: Channel;
10
+ /** Array of Pyth Lazer price feed IDs to subscribe to */
11
+ priceFeedIds: number[];
12
+ };
13
+ /**
14
+ * Manages subscriptions to Pyth Lazer price feeds and provides access to real-time price data.
15
+ * Automatically filters out non-stable feeds and handles reconnection logic.
16
+ */
17
+ export declare class PythLazerSubscriber {
18
+ private endpoints;
19
+ private token;
20
+ private priceFeedArrays;
21
+ private resubTimeoutMs;
22
+ private sdkLogging;
23
+ private static readonly SYMBOLS_API_URL;
24
+ private symbolsCache;
25
+ private pythLazerClient?;
26
+ feedIdChunkToPriceMessage: Map<string, string>;
27
+ feedIdToPrice: Map<number, number>;
28
+ feedIdHashToFeedIds: Map<string, number[]>;
29
+ subscriptionIdsToFeedIdsHash: Map<number, string>;
30
+ allSubscribedIds: number[];
31
+ timeoutId?: NodeJS.Timeout;
32
+ receivingData: boolean;
33
+ isUnsubscribing: boolean;
34
+ marketIndextoPriceFeedIdChunk: Map<number, number[]>;
35
+ marketIndextoPriceFeedId: Map<number, number>;
36
+ /**
37
+ * Creates a new PythLazerSubscriber instance.
38
+ * @param endpoints - Array of WebSocket endpoint URLs for Pyth Lazer
39
+ * @param token - Authentication token for Pyth Lazer API
40
+ * @param priceFeedArrays - Array of price feed configurations to subscribe to
41
+ * @param env - Drift environment (mainnet-beta, devnet, etc.)
42
+ * @param resubTimeoutMs - Milliseconds to wait before resubscribing on data timeout
43
+ * @param sdkLogging - Whether to log Pyth SDK logs to the console. This is very noisy but could be useful for debugging.
44
+ */
45
+ constructor(endpoints: string[], token: string, priceFeedArrays: PythLazerPriceFeedArray[], env?: DriftEnv, resubTimeoutMs?: number, sdkLogging?: boolean);
46
+ private fetchSymbolsIfNeeded;
47
+ private filterStableFeeds;
48
+ /**
49
+ * Subscribes to Pyth Lazer price feeds. Automatically filters out non-stable feeds
50
+ * and establishes WebSocket connections for real-time price updates.
51
+ */
52
+ subscribe(): Promise<void>;
53
+ protected setTimeout(): void;
54
+ /**
55
+ * Unsubscribes from all Pyth Lazer price feeds and shuts down WebSocket connections.
56
+ */
57
+ unsubscribe(): Promise<void>;
58
+ hash(arr: number[]): string;
59
+ /**
60
+ * Retrieves the latest Solana-format price message for a group of feed IDs.
61
+ * @param feedIds - Array of price feed IDs
62
+ * @returns Hex-encoded price message data, or undefined if not available
63
+ */
64
+ getLatestPriceMessage(feedIds: number[]): Promise<string | undefined>;
65
+ /**
66
+ * Retrieves the latest Solana-format price message for a specific market.
67
+ * @param marketIndex - The market index to get price data for
68
+ * @returns Hex-encoded price message data, or undefined if not found
69
+ */
70
+ getLatestPriceMessageForMarketIndex(marketIndex: number): Promise<string | undefined>;
71
+ /**
72
+ * Gets the array of price feed IDs associated with a market index.
73
+ * @param marketIndex - The market index to look up
74
+ * @returns Array of price feed IDs, or empty array if not found
75
+ */
76
+ getPriceFeedIdsFromMarketIndex(marketIndex: number): number[];
77
+ /**
78
+ * Gets the array of price feed IDs from a subscription hash.
79
+ * @param hash - The subscription hash
80
+ * @returns Array of price feed IDs, or empty array if not found
81
+ */
82
+ getPriceFeedIdsFromHash(hash: string): number[];
83
+ /**
84
+ * Gets the current parsed price for a specific market index.
85
+ * @param marketIndex - The market index to get the price for
86
+ * @returns The price as a number, or undefined if not available
87
+ */
88
+ getPriceFromMarketIndex(marketIndex: number): number | undefined;
89
+ }
90
+ //# sourceMappingURL=pythLazerSubscriber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pythLazerSubscriber.d.ts","sourceRoot":"","sources":["../../../src/pyth/pythLazerSubscriber.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAmB,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAGrC;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACrC,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yDAAyD;IACzD,YAAY,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAOF;;;GAGG;AACH,qBAAa,mBAAmB;IA4B9B,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,eAAe;IAEvB,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,UAAU;IAhCnB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CACwB;IAC/D,OAAO,CAAC,YAAY,CAA4C;IAChE,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,yBAAyB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAa;IAC3D,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAa;IAC/C,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAa;IACvD,4BAA4B,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAa;IAC9D,gBAAgB,EAAE,MAAM,EAAE,CAAM;IAEhC,SAAS,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;IAC3B,aAAa,UAAS;IACtB,eAAe,UAAS;IAExB,6BAA6B,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAa;IACjE,wBAAwB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAa;IAE1D;;;;;;;;OAQG;gBAEM,SAAS,EAAE,MAAM,EAAE,EACnB,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,uBAAuB,EAAE,EAClD,GAAG,GAAE,QAAmB,EAChB,cAAc,GAAE,MAAa,EAC7B,UAAU,GAAE,OAAe;YA2BtB,oBAAoB;IAuBlC,OAAO,CAAC,iBAAiB;IAuBzB;;;OAGG;IACG,SAAS;IA0Hf,SAAS,CAAC,UAAU,IAAI,IAAI;IAgB5B;;OAEG;IACG,WAAW;IASjB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM;IAI3B;;;;OAIG;IACG,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAI3E;;;;OAIG;IACG,mCAAmC,CACxC,WAAW,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAQ9B;;;;OAIG;IACH,8BAA8B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE;IAI7D;;;;OAIG;IACH,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAI/C;;;;OAIG;IACH,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;CAOhE"}
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PythLazerSubscriber = void 0;
4
+ const pyth_lazer_sdk_1 = require("@pythnetwork/pyth-lazer-sdk");
5
+ const perpMarkets_1 = require("../constants/perpMarkets");
6
+ /**
7
+ * Manages subscriptions to Pyth Lazer price feeds and provides access to real-time price data.
8
+ * Automatically filters out non-stable feeds and handles reconnection logic.
9
+ */
10
+ class PythLazerSubscriber {
11
+ /**
12
+ * Creates a new PythLazerSubscriber instance.
13
+ * @param endpoints - Array of WebSocket endpoint URLs for Pyth Lazer
14
+ * @param token - Authentication token for Pyth Lazer API
15
+ * @param priceFeedArrays - Array of price feed configurations to subscribe to
16
+ * @param env - Drift environment (mainnet-beta, devnet, etc.)
17
+ * @param resubTimeoutMs - Milliseconds to wait before resubscribing on data timeout
18
+ * @param sdkLogging - Whether to log Pyth SDK logs to the console. This is very noisy but could be useful for debugging.
19
+ */
20
+ constructor(endpoints, token, priceFeedArrays, env = 'devnet', resubTimeoutMs = 2000, sdkLogging = false) {
21
+ this.endpoints = endpoints;
22
+ this.token = token;
23
+ this.priceFeedArrays = priceFeedArrays;
24
+ this.resubTimeoutMs = resubTimeoutMs;
25
+ this.sdkLogging = sdkLogging;
26
+ this.symbolsCache = null;
27
+ this.feedIdChunkToPriceMessage = new Map();
28
+ this.feedIdToPrice = new Map();
29
+ this.feedIdHashToFeedIds = new Map();
30
+ this.subscriptionIdsToFeedIdsHash = new Map();
31
+ this.allSubscribedIds = [];
32
+ this.receivingData = false;
33
+ this.isUnsubscribing = false;
34
+ this.marketIndextoPriceFeedIdChunk = new Map();
35
+ this.marketIndextoPriceFeedId = new Map();
36
+ const markets = perpMarkets_1.PerpMarkets[env].filter((market) => market.pythLazerId !== undefined);
37
+ this.allSubscribedIds = this.priceFeedArrays
38
+ .map((array) => array.priceFeedIds)
39
+ .flat();
40
+ for (const priceFeedIds of priceFeedArrays) {
41
+ const filteredMarkets = markets.filter((market) => priceFeedIds.priceFeedIds.includes(market.pythLazerId));
42
+ for (const market of filteredMarkets) {
43
+ this.marketIndextoPriceFeedIdChunk.set(market.marketIndex, priceFeedIds.priceFeedIds);
44
+ this.marketIndextoPriceFeedId.set(market.marketIndex, market.pythLazerId);
45
+ }
46
+ }
47
+ }
48
+ async fetchSymbolsIfNeeded() {
49
+ if (this.symbolsCache !== null)
50
+ return;
51
+ try {
52
+ const response = await fetch(PythLazerSubscriber.SYMBOLS_API_URL);
53
+ if (!response.ok)
54
+ throw new Error(`HTTP ${response.status}`);
55
+ const symbols = await response.json();
56
+ this.symbolsCache = new Map();
57
+ for (const symbol of symbols) {
58
+ this.symbolsCache.set(symbol.pyth_lazer_id, {
59
+ name: symbol.name,
60
+ state: symbol.state,
61
+ });
62
+ }
63
+ }
64
+ catch (error) {
65
+ console.warn(`Failed to fetch Pyth Lazer symbols, proceeding with all feeds: ${error}`);
66
+ this.symbolsCache = new Map(); // Empty map = no filtering
67
+ }
68
+ }
69
+ filterStableFeeds(feedIds) {
70
+ if (this.symbolsCache === null || this.symbolsCache.size === 0) {
71
+ return feedIds; // No filtering if cache unavailable
72
+ }
73
+ return feedIds.filter((feedId) => {
74
+ const info = this.symbolsCache.get(feedId);
75
+ if (!info) {
76
+ console.warn(`Feed ID ${feedId} not found in symbols API, including anyway`);
77
+ return true;
78
+ }
79
+ if (info.state !== 'stable') {
80
+ console.warn(`Removing feed ID ${feedId} (${info.name}) - state is "${info.state}", not "stable"`);
81
+ return false;
82
+ }
83
+ return true;
84
+ });
85
+ }
86
+ /**
87
+ * Subscribes to Pyth Lazer price feeds. Automatically filters out non-stable feeds
88
+ * and establishes WebSocket connections for real-time price updates.
89
+ */
90
+ async subscribe() {
91
+ var _a;
92
+ await this.fetchSymbolsIfNeeded();
93
+ this.pythLazerClient = await pyth_lazer_sdk_1.PythLazerClient.create({
94
+ token: this.token,
95
+ logger: this.sdkLogging ? console : undefined,
96
+ webSocketPoolConfig: {
97
+ urls: this.endpoints,
98
+ numConnections: 4, // Optionally specify number of parallel redundant connections to reduce the chance of dropped messages. The connections will round-robin across the provided URLs. Default is 4.
99
+ onError: (error) => {
100
+ console.error('⛔️ PythLazerClient error:', error.message);
101
+ },
102
+ onWebSocketError: (error) => {
103
+ console.error('⛔️ WebSocket error:', error.message);
104
+ },
105
+ onWebSocketPoolError: (error) => {
106
+ console.error('⛔️ WebSocket pool error:', error.message);
107
+ },
108
+ // Optional configuration for resilient WebSocket connections
109
+ rwsConfig: {
110
+ heartbeatTimeoutDurationMs: 5000, // Optional heartbeat timeout duration in milliseconds
111
+ maxRetryDelayMs: 1000, // Optional maximum retry delay in milliseconds
112
+ logAfterRetryCount: 10, // Optional log after how many retries
113
+ },
114
+ },
115
+ });
116
+ // Reset allSubscribedIds to rebuild with only stable feeds
117
+ this.allSubscribedIds = [];
118
+ let subscriptionId = 1;
119
+ for (const priceFeedArray of this.priceFeedArrays) {
120
+ const filteredFeedIds = this.filterStableFeeds(priceFeedArray.priceFeedIds);
121
+ if (filteredFeedIds.length === 0) {
122
+ console.warn(`All feeds filtered out for subscription ${subscriptionId}, skipping`);
123
+ continue;
124
+ }
125
+ // Update allSubscribedIds with only stable feeds
126
+ this.allSubscribedIds.push(...filteredFeedIds);
127
+ const feedIdsHash = this.hash(filteredFeedIds);
128
+ this.feedIdHashToFeedIds.set(feedIdsHash, filteredFeedIds);
129
+ this.subscriptionIdsToFeedIdsHash.set(subscriptionId, feedIdsHash);
130
+ // Update marketIndextoPriceFeedIdChunk to use filtered feeds
131
+ for (const [marketIndex, chunk,] of this.marketIndextoPriceFeedIdChunk.entries()) {
132
+ if (this.hash(chunk) === this.hash(priceFeedArray.priceFeedIds)) {
133
+ this.marketIndextoPriceFeedIdChunk.set(marketIndex, filteredFeedIds);
134
+ }
135
+ }
136
+ // Remove entries from marketIndextoPriceFeedId for filtered-out feeds
137
+ for (const [marketIndex, feedId,] of this.marketIndextoPriceFeedId.entries()) {
138
+ if (!filteredFeedIds.includes(feedId) &&
139
+ priceFeedArray.priceFeedIds.includes(feedId)) {
140
+ this.marketIndextoPriceFeedId.delete(marketIndex);
141
+ this.marketIndextoPriceFeedIdChunk.delete(marketIndex);
142
+ }
143
+ }
144
+ this.pythLazerClient.addMessageListener((message) => {
145
+ var _a, _b;
146
+ this.receivingData = true;
147
+ clearTimeout(this.timeoutId);
148
+ switch (message.type) {
149
+ case 'json': {
150
+ if (message.value.type == 'streamUpdated') {
151
+ if ((_a = message.value.solana) === null || _a === void 0 ? void 0 : _a.data) {
152
+ this.feedIdChunkToPriceMessage.set(this.subscriptionIdsToFeedIdsHash.get(message.value.subscriptionId), message.value.solana.data);
153
+ }
154
+ if ((_b = message.value.parsed) === null || _b === void 0 ? void 0 : _b.priceFeeds) {
155
+ for (const priceFeed of message.value.parsed.priceFeeds) {
156
+ const price = Number(priceFeed.price) *
157
+ Math.pow(10, Number(priceFeed.exponent));
158
+ this.feedIdToPrice.set(priceFeed.priceFeedId, price);
159
+ }
160
+ }
161
+ }
162
+ break;
163
+ }
164
+ default: {
165
+ break;
166
+ }
167
+ }
168
+ this.setTimeout();
169
+ });
170
+ this.pythLazerClient.send({
171
+ type: 'subscribe',
172
+ subscriptionId,
173
+ priceFeedIds: filteredFeedIds,
174
+ properties: ['price', 'bestAskPrice', 'bestBidPrice', 'exponent'],
175
+ formats: ['solana'],
176
+ deliveryFormat: 'json',
177
+ channel: (_a = priceFeedArray.channel) !== null && _a !== void 0 ? _a : 'fixed_rate@200ms',
178
+ jsonBinaryEncoding: 'hex',
179
+ });
180
+ subscriptionId++;
181
+ }
182
+ this.receivingData = true;
183
+ this.setTimeout();
184
+ }
185
+ setTimeout() {
186
+ this.timeoutId = setTimeout(async () => {
187
+ if (this.isUnsubscribing) {
188
+ // If we are in the process of unsubscribing, do not attempt to resubscribe
189
+ return;
190
+ }
191
+ if (this.receivingData) {
192
+ console.log(`No ws data from pyth lazer client resubscribing`);
193
+ await this.unsubscribe();
194
+ this.receivingData = false;
195
+ await this.subscribe();
196
+ }
197
+ }, this.resubTimeoutMs);
198
+ }
199
+ /**
200
+ * Unsubscribes from all Pyth Lazer price feeds and shuts down WebSocket connections.
201
+ */
202
+ async unsubscribe() {
203
+ var _a;
204
+ this.isUnsubscribing = true;
205
+ (_a = this.pythLazerClient) === null || _a === void 0 ? void 0 : _a.shutdown();
206
+ this.pythLazerClient = undefined;
207
+ clearTimeout(this.timeoutId);
208
+ this.timeoutId = undefined;
209
+ this.isUnsubscribing = false;
210
+ }
211
+ hash(arr) {
212
+ return 'h:' + arr.join('|');
213
+ }
214
+ /**
215
+ * Retrieves the latest Solana-format price message for a group of feed IDs.
216
+ * @param feedIds - Array of price feed IDs
217
+ * @returns Hex-encoded price message data, or undefined if not available
218
+ */
219
+ async getLatestPriceMessage(feedIds) {
220
+ return this.feedIdChunkToPriceMessage.get(this.hash(feedIds));
221
+ }
222
+ /**
223
+ * Retrieves the latest Solana-format price message for a specific market.
224
+ * @param marketIndex - The market index to get price data for
225
+ * @returns Hex-encoded price message data, or undefined if not found
226
+ */
227
+ async getLatestPriceMessageForMarketIndex(marketIndex) {
228
+ const feedIds = this.marketIndextoPriceFeedIdChunk.get(marketIndex);
229
+ if (!feedIds) {
230
+ return undefined;
231
+ }
232
+ return await this.getLatestPriceMessage(feedIds);
233
+ }
234
+ /**
235
+ * Gets the array of price feed IDs associated with a market index.
236
+ * @param marketIndex - The market index to look up
237
+ * @returns Array of price feed IDs, or empty array if not found
238
+ */
239
+ getPriceFeedIdsFromMarketIndex(marketIndex) {
240
+ return this.marketIndextoPriceFeedIdChunk.get(marketIndex) || [];
241
+ }
242
+ /**
243
+ * Gets the array of price feed IDs from a subscription hash.
244
+ * @param hash - The subscription hash
245
+ * @returns Array of price feed IDs, or empty array if not found
246
+ */
247
+ getPriceFeedIdsFromHash(hash) {
248
+ return this.feedIdHashToFeedIds.get(hash) || [];
249
+ }
250
+ /**
251
+ * Gets the current parsed price for a specific market index.
252
+ * @param marketIndex - The market index to get the price for
253
+ * @returns The price as a number, or undefined if not available
254
+ */
255
+ getPriceFromMarketIndex(marketIndex) {
256
+ const feedId = this.marketIndextoPriceFeedId.get(marketIndex);
257
+ if (feedId === undefined) {
258
+ return undefined;
259
+ }
260
+ return this.feedIdToPrice.get(feedId);
261
+ }
262
+ }
263
+ exports.PythLazerSubscriber = PythLazerSubscriber;
264
+ PythLazerSubscriber.SYMBOLS_API_URL = 'https://history.pyth-lazer.dourolabs.app/history/v1/symbols';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.156.0-beta.1",
3
+ "version": "2.156.0-beta.3",
4
4
  "main": "lib/node/index.js",
5
5
  "types": "lib/node/index.d.ts",
6
6
  "module": "./lib/browser/index.js",
@@ -2,8 +2,13 @@ import { Channel, PythLazerClient } from '@pythnetwork/pyth-lazer-sdk';
2
2
  import { DriftEnv } from '../config';
3
3
  import { PerpMarkets } from '../constants/perpMarkets';
4
4
 
5
+ /**
6
+ * Configuration for a group of Pyth Lazer price feeds.
7
+ */
5
8
  export type PythLazerPriceFeedArray = {
9
+ /** Optional channel for update frequency (e.g., 'fixed_rate@200ms') */
6
10
  channel?: Channel;
11
+ /** Array of Pyth Lazer price feed IDs to subscribe to */
7
12
  priceFeedIds: number[];
8
13
  };
9
14
 
@@ -12,6 +17,10 @@ type FeedSymbolInfo = {
12
17
  state: string;
13
18
  };
14
19
 
20
+ /**
21
+ * Manages subscriptions to Pyth Lazer price feeds and provides access to real-time price data.
22
+ * Automatically filters out non-stable feeds and handles reconnection logic.
23
+ */
15
24
  export class PythLazerSubscriber {
16
25
  private static readonly SYMBOLS_API_URL =
17
26
  'https://history.pyth-lazer.dourolabs.app/history/v1/symbols';
@@ -30,15 +39,21 @@ export class PythLazerSubscriber {
30
39
  marketIndextoPriceFeedIdChunk: Map<number, number[]> = new Map();
31
40
  marketIndextoPriceFeedId: Map<number, number> = new Map();
32
41
 
42
+ /**
43
+ * Creates a new PythLazerSubscriber instance.
44
+ * @param endpoints - Array of WebSocket endpoint URLs for Pyth Lazer
45
+ * @param token - Authentication token for Pyth Lazer API
46
+ * @param priceFeedArrays - Array of price feed configurations to subscribe to
47
+ * @param env - Drift environment (mainnet-beta, devnet, etc.)
48
+ * @param resubTimeoutMs - Milliseconds to wait before resubscribing on data timeout
49
+ * @param sdkLogging - Whether to log Pyth SDK logs to the console. This is very noisy but could be useful for debugging.
50
+ */
33
51
  constructor(
34
52
  private endpoints: string[],
35
53
  private token: string,
36
54
  private priceFeedArrays: PythLazerPriceFeedArray[],
37
55
  env: DriftEnv = 'devnet',
38
56
  private resubTimeoutMs: number = 2000,
39
- /**
40
- * Whether to log Pyth SDK logs to the console. This is very noisy but could be useful for debugging.
41
- */
42
57
  private sdkLogging: boolean = false
43
58
  ) {
44
59
  const markets = PerpMarkets[env].filter(
@@ -112,6 +127,10 @@ export class PythLazerSubscriber {
112
127
  });
113
128
  }
114
129
 
130
+ /**
131
+ * Subscribes to Pyth Lazer price feeds. Automatically filters out non-stable feeds
132
+ * and establishes WebSocket connections for real-time price updates.
133
+ */
115
134
  async subscribe() {
116
135
  await this.fetchSymbolsIfNeeded();
117
136
 
@@ -250,6 +269,9 @@ export class PythLazerSubscriber {
250
269
  }, this.resubTimeoutMs);
251
270
  }
252
271
 
272
+ /**
273
+ * Unsubscribes from all Pyth Lazer price feeds and shuts down WebSocket connections.
274
+ */
253
275
  async unsubscribe() {
254
276
  this.isUnsubscribing = true;
255
277
  this.pythLazerClient?.shutdown();
@@ -263,10 +285,20 @@ export class PythLazerSubscriber {
263
285
  return 'h:' + arr.join('|');
264
286
  }
265
287
 
288
+ /**
289
+ * Retrieves the latest Solana-format price message for a group of feed IDs.
290
+ * @param feedIds - Array of price feed IDs
291
+ * @returns Hex-encoded price message data, or undefined if not available
292
+ */
266
293
  async getLatestPriceMessage(feedIds: number[]): Promise<string | undefined> {
267
294
  return this.feedIdChunkToPriceMessage.get(this.hash(feedIds));
268
295
  }
269
296
 
297
+ /**
298
+ * Retrieves the latest Solana-format price message for a specific market.
299
+ * @param marketIndex - The market index to get price data for
300
+ * @returns Hex-encoded price message data, or undefined if not found
301
+ */
270
302
  async getLatestPriceMessageForMarketIndex(
271
303
  marketIndex: number
272
304
  ): Promise<string | undefined> {
@@ -277,14 +309,29 @@ export class PythLazerSubscriber {
277
309
  return await this.getLatestPriceMessage(feedIds);
278
310
  }
279
311
 
312
+ /**
313
+ * Gets the array of price feed IDs associated with a market index.
314
+ * @param marketIndex - The market index to look up
315
+ * @returns Array of price feed IDs, or empty array if not found
316
+ */
280
317
  getPriceFeedIdsFromMarketIndex(marketIndex: number): number[] {
281
318
  return this.marketIndextoPriceFeedIdChunk.get(marketIndex) || [];
282
319
  }
283
320
 
321
+ /**
322
+ * Gets the array of price feed IDs from a subscription hash.
323
+ * @param hash - The subscription hash
324
+ * @returns Array of price feed IDs, or empty array if not found
325
+ */
284
326
  getPriceFeedIdsFromHash(hash: string): number[] {
285
327
  return this.feedIdHashToFeedIds.get(hash) || [];
286
328
  }
287
329
 
330
+ /**
331
+ * Gets the current parsed price for a specific market index.
332
+ * @param marketIndex - The market index to get the price for
333
+ * @returns The price as a number, or undefined if not available
334
+ */
288
335
  getPriceFromMarketIndex(marketIndex: number): number | undefined {
289
336
  const feedId = this.marketIndextoPriceFeedId.get(marketIndex);
290
337
  if (feedId === undefined) {