@pythnetwork/price-service-client 1.9.1 → 1.10.0

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.
@@ -0,0 +1,310 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable unicorn/no-process-exit */ /* eslint-disable n/no-process-exit */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/no-unnecessary-condition */ /* eslint-disable @typescript-eslint/no-base-to-string */ /* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-empty-function */ /* eslint-disable no-console */ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "PriceServiceConnection", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return PriceServiceConnection;
9
+ }
10
+ });
11
+ const _priceservicesdk = require("@pythnetwork/price-service-sdk");
12
+ const _axios = /*#__PURE__*/ _interop_require_default(require("axios"));
13
+ const _axiosretry = /*#__PURE__*/ _interop_require_default(require("axios-retry"));
14
+ const _ResillientWebSocket = require("./ResillientWebSocket.cjs");
15
+ const _utils = require("./utils.cjs");
16
+ function _interop_require_default(obj) {
17
+ return obj && obj.__esModule ? obj : {
18
+ default: obj
19
+ };
20
+ }
21
+ class PriceServiceConnection {
22
+ httpClient;
23
+ priceFeedCallbacks;
24
+ wsClient;
25
+ wsEndpoint;
26
+ logger;
27
+ priceFeedRequestConfig;
28
+ /**
29
+ * Custom handler for web socket errors (connection and message parsing).
30
+ *
31
+ * Default handler only logs the errors.
32
+ */ onWsError;
33
+ /**
34
+ * Constructs a new Connection.
35
+ *
36
+ * @param endpoint - endpoint URL to the price service. Example: https://website/example/
37
+ * @param config - Optional PriceServiceConnectionConfig for custom configurations.
38
+ */ constructor(endpoint, config){
39
+ this.httpClient = _axios.default.create({
40
+ baseURL: endpoint,
41
+ timeout: config?.timeout || 5000
42
+ });
43
+ (0, _axiosretry.default)(this.httpClient, {
44
+ retries: config?.httpRetries || 3,
45
+ // eslint-disable-next-line import/no-named-as-default-member
46
+ retryDelay: _axiosretry.default.exponentialDelay.bind(_axiosretry.default)
47
+ });
48
+ this.priceFeedRequestConfig = {
49
+ binary: config?.priceFeedRequestConfig?.binary,
50
+ verbose: config?.priceFeedRequestConfig?.verbose ?? config?.verbose,
51
+ allowOutOfOrder: config?.priceFeedRequestConfig?.allowOutOfOrder
52
+ };
53
+ this.priceFeedCallbacks = new Map();
54
+ // Default logger is console for only warnings and errors.
55
+ this.logger = config?.logger || {
56
+ trace: ()=>{},
57
+ debug: ()=>{},
58
+ info: ()=>{},
59
+ warn: console.warn,
60
+ error: console.error
61
+ };
62
+ this.onWsError = (error)=>{
63
+ this.logger.error(error);
64
+ // Exit the process if it is running in node.
65
+ if (typeof process !== "undefined" && typeof process.exit === "function") {
66
+ this.logger.error("Halting the process due to the websocket error");
67
+ process.exit(1);
68
+ } else {
69
+ this.logger.error("Cannot halt process. Please handle the websocket error.");
70
+ }
71
+ };
72
+ this.wsEndpoint = (0, _utils.makeWebsocketUrl)(endpoint);
73
+ }
74
+ /**
75
+ * Fetch Latest PriceFeeds of given price ids.
76
+ * This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price ids)
77
+ *
78
+ * @param priceIds - Array of hex-encoded price ids.
79
+ * @returns Array of PriceFeeds
80
+ */ async getLatestPriceFeeds(priceIds) {
81
+ if (priceIds.length === 0) {
82
+ return [];
83
+ }
84
+ const response = await this.httpClient.get("/api/latest_price_feeds", {
85
+ params: {
86
+ ids: priceIds,
87
+ verbose: this.priceFeedRequestConfig.verbose,
88
+ binary: this.priceFeedRequestConfig.binary
89
+ }
90
+ });
91
+ const priceFeedsJson = response.data;
92
+ return priceFeedsJson.map((priceFeedJson)=>_priceservicesdk.PriceFeed.fromJson(priceFeedJson));
93
+ }
94
+ /**
95
+ * Fetch latest VAA of given price ids.
96
+ * This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price ids)
97
+ *
98
+ * This function is coupled to wormhole implemntation.
99
+ *
100
+ * @param priceIds - Array of hex-encoded price ids.
101
+ * @returns Array of base64 encoded VAAs.
102
+ */ async getLatestVaas(priceIds) {
103
+ const response = await this.httpClient.get("/api/latest_vaas", {
104
+ params: {
105
+ ids: priceIds
106
+ }
107
+ });
108
+ return response.data;
109
+ }
110
+ /**
111
+ * Fetch the earliest VAA of the given price id that is published since the given publish time.
112
+ * This will throw an error if the given publish time is in the future, or if the publish time
113
+ * is old and the price service endpoint does not have a db backend for historical requests.
114
+ * This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price id)
115
+ *
116
+ * This function is coupled to wormhole implemntation.
117
+ *
118
+ * @param priceId - Hex-encoded price id.
119
+ * @param publishTime - Epoch timestamp in seconds.
120
+ * @returns Tuple of VAA and publishTime.
121
+ */ async getVaa(priceId, publishTime) {
122
+ const response = await this.httpClient.get("/api/get_vaa", {
123
+ params: {
124
+ id: priceId,
125
+ publish_time: publishTime
126
+ }
127
+ });
128
+ return [
129
+ response.data.vaa,
130
+ response.data.publishTime
131
+ ];
132
+ }
133
+ /**
134
+ * Fetch the PriceFeed of the given price id that is published since the given publish time.
135
+ * This will throw an error if the given publish time is in the future, or if the publish time
136
+ * is old and the price service endpoint does not have a db backend for historical requests.
137
+ * This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price id)
138
+ *
139
+ * @param priceId - Hex-encoded price id.
140
+ * @param publishTime - Epoch timestamp in seconds.
141
+ * @returns PriceFeed
142
+ */ async getPriceFeed(priceId, publishTime) {
143
+ const response = await this.httpClient.get("/api/get_price_feed", {
144
+ params: {
145
+ id: priceId,
146
+ publish_time: publishTime,
147
+ verbose: this.priceFeedRequestConfig.verbose,
148
+ binary: this.priceFeedRequestConfig.binary
149
+ }
150
+ });
151
+ return _priceservicesdk.PriceFeed.fromJson(response.data);
152
+ }
153
+ /**
154
+ * Fetch the list of available price feed ids.
155
+ * This will throw an axios error if there is a network problem or the price service returns a non-ok response.
156
+ *
157
+ * @returns Array of hex-encoded price ids.
158
+ */ async getPriceFeedIds() {
159
+ const response = await this.httpClient.get("/api/price_feed_ids");
160
+ return response.data;
161
+ }
162
+ /**
163
+ * Subscribe to updates for given price ids.
164
+ *
165
+ * It will start a websocket connection if it's not started yet.
166
+ * Also, it won't throw any exception if given price ids are invalid or connection errors. Instead,
167
+ * it calls `connection.onWsError`. If you want to handle the errors you should set the
168
+ * `onWsError` function to your custom error handler.
169
+ *
170
+ * @param priceIds - Array of hex-encoded price ids.
171
+ * @param cb - Callback function that is called with a PriceFeed upon updates to given price ids.
172
+ */ async subscribePriceFeedUpdates(priceIds, cb) {
173
+ if (this.wsClient === undefined) {
174
+ await this.startWebSocket();
175
+ }
176
+ priceIds = priceIds.map((priceId)=>(0, _utils.removeLeading0xIfExists)(priceId));
177
+ const newPriceIds = [];
178
+ for (const id of priceIds){
179
+ if (!this.priceFeedCallbacks.has(id)) {
180
+ this.priceFeedCallbacks.set(id, new Set());
181
+ newPriceIds.push(id);
182
+ }
183
+ this.priceFeedCallbacks.get(id).add(cb);
184
+ }
185
+ const message = {
186
+ ids: newPriceIds,
187
+ type: "subscribe",
188
+ verbose: this.priceFeedRequestConfig.verbose,
189
+ binary: this.priceFeedRequestConfig.binary,
190
+ allow_out_of_order: this.priceFeedRequestConfig.allowOutOfOrder
191
+ };
192
+ await this.wsClient?.send(JSON.stringify(message));
193
+ }
194
+ /**
195
+ * Unsubscribe from updates for given price ids.
196
+ *
197
+ * It will close the websocket connection if it's not subscribed to any price feed updates anymore.
198
+ * Also, it won't throw any exception if given price ids are invalid or connection errors. Instead,
199
+ * it calls `connection.onWsError`. If you want to handle the errors you should set the
200
+ * `onWsError` function to your custom error handler.
201
+ *
202
+ * @param priceIds - Array of hex-encoded price ids.
203
+ * @param cb - Optional callback, if set it will only unsubscribe this callback from updates for given price ids.
204
+ */ async unsubscribePriceFeedUpdates(priceIds, cb) {
205
+ if (this.wsClient === undefined) {
206
+ await this.startWebSocket();
207
+ }
208
+ priceIds = priceIds.map((priceId)=>(0, _utils.removeLeading0xIfExists)(priceId));
209
+ const removedPriceIds = [];
210
+ for (const id of priceIds){
211
+ if (this.priceFeedCallbacks.has(id)) {
212
+ let idRemoved = false;
213
+ if (cb === undefined) {
214
+ this.priceFeedCallbacks.delete(id);
215
+ idRemoved = true;
216
+ } else {
217
+ this.priceFeedCallbacks.get(id).delete(cb);
218
+ if (this.priceFeedCallbacks.get(id).size === 0) {
219
+ this.priceFeedCallbacks.delete(id);
220
+ idRemoved = true;
221
+ }
222
+ }
223
+ if (idRemoved) {
224
+ removedPriceIds.push(id);
225
+ }
226
+ }
227
+ }
228
+ const message = {
229
+ ids: removedPriceIds,
230
+ type: "unsubscribe"
231
+ };
232
+ await this.wsClient?.send(JSON.stringify(message));
233
+ if (this.priceFeedCallbacks.size === 0) {
234
+ this.closeWebSocket();
235
+ }
236
+ }
237
+ /**
238
+ * Starts connection websocket.
239
+ *
240
+ * This function is called automatically upon subscribing to price feed updates.
241
+ */ async startWebSocket() {
242
+ if (this.wsEndpoint === undefined) {
243
+ throw new Error("Websocket endpoint is undefined.");
244
+ }
245
+ this.wsClient = new _ResillientWebSocket.ResilientWebSocket(this.wsEndpoint, this.logger);
246
+ this.wsClient.onError = this.onWsError;
247
+ this.wsClient.onReconnect = ()=>{
248
+ if (this.priceFeedCallbacks.size > 0) {
249
+ const message = {
250
+ ids: [
251
+ ...this.priceFeedCallbacks.keys()
252
+ ],
253
+ type: "subscribe",
254
+ verbose: this.priceFeedRequestConfig.verbose,
255
+ binary: this.priceFeedRequestConfig.binary,
256
+ allow_out_of_order: this.priceFeedRequestConfig.allowOutOfOrder
257
+ };
258
+ this.logger.info("Resubscribing to existing price feeds.");
259
+ this.wsClient?.send(JSON.stringify(message));
260
+ }
261
+ };
262
+ this.wsClient.onMessage = (data)=>{
263
+ this.logger.info(`Received message ${data.toString()}`);
264
+ let message;
265
+ try {
266
+ message = JSON.parse(data.toString());
267
+ } catch (error) {
268
+ this.logger.error(`Error parsing message ${data.toString()} as JSON.`);
269
+ this.logger.error(error);
270
+ this.onWsError(error);
271
+ return;
272
+ }
273
+ if (message.type === "response") {
274
+ if (message.status === "error") {
275
+ this.logger.error(`Error response from the websocket server ${message.error}.`);
276
+ this.onWsError(new Error(message.error));
277
+ }
278
+ } else if (message.type === "price_update") {
279
+ let priceFeed;
280
+ try {
281
+ priceFeed = _priceservicesdk.PriceFeed.fromJson(message.price_feed);
282
+ } catch (error) {
283
+ this.logger.error(`Error parsing price feeds from message ${data.toString()}.`);
284
+ this.logger.error(error);
285
+ this.onWsError(error);
286
+ return;
287
+ }
288
+ if (this.priceFeedCallbacks.has(priceFeed.id)) {
289
+ for (const cb of this.priceFeedCallbacks.get(priceFeed.id)){
290
+ cb(priceFeed);
291
+ }
292
+ }
293
+ } else {
294
+ this.logger.warn(`Ignoring unsupported server response ${data.toString()}.`);
295
+ }
296
+ };
297
+ await this.wsClient.startWebSocket();
298
+ }
299
+ /**
300
+ * Closes connection websocket.
301
+ *
302
+ * At termination, the websocket should be closed to finish the
303
+ * process elegantly. It will automatically close when the connection
304
+ * is subscribed to no price feeds.
305
+ */ closeWebSocket() {
306
+ this.wsClient?.closeWebSocket();
307
+ this.wsClient = undefined;
308
+ this.priceFeedCallbacks.clear();
309
+ }
310
+ }
@@ -1,23 +1,24 @@
1
- import { HexString, PriceFeed } from "@pythnetwork/price-service-sdk";
2
- import { Logger } from "ts-log";
1
+ import type { HexString } from "@pythnetwork/price-service-sdk";
2
+ import { PriceFeed } from "@pythnetwork/price-service-sdk";
3
+ import type { Logger } from "ts-log";
3
4
  export type DurationInMs = number;
4
5
  export type PriceFeedRequestConfig = {
5
- verbose?: boolean;
6
- binary?: boolean;
7
- allowOutOfOrder?: boolean;
6
+ verbose?: boolean | undefined;
7
+ binary?: boolean | undefined;
8
+ allowOutOfOrder?: boolean | undefined;
8
9
  };
9
10
  export type PriceServiceConnectionConfig = {
10
- timeout?: DurationInMs;
11
+ timeout?: DurationInMs | undefined;
11
12
  /**
12
13
  * Number of times a HTTP request will be retried before the API returns a failure. Default: 3.
13
14
  *
14
15
  * The connection uses exponential back-off for the delay between retries. However,
15
16
  * it will timeout regardless of the retries at the configured `timeout` time.
16
17
  */
17
- httpRetries?: number;
18
- logger?: Logger;
19
- verbose?: boolean;
20
- priceFeedRequestConfig?: PriceFeedRequestConfig;
18
+ httpRetries?: number | undefined;
19
+ logger?: Logger | undefined;
20
+ verbose?: boolean | undefined;
21
+ priceFeedRequestConfig?: PriceFeedRequestConfig | undefined;
21
22
  };
22
23
  export type PriceFeedUpdateCallback = (priceFeed: PriceFeed) => void;
23
24
  export declare class PriceServiceConnection {
@@ -36,15 +37,15 @@ export declare class PriceServiceConnection {
36
37
  /**
37
38
  * Constructs a new Connection.
38
39
  *
39
- * @param endpoint endpoint URL to the price service. Example: https://website/example/
40
- * @param config Optional PriceServiceConnectionConfig for custom configurations.
40
+ * @param endpoint - endpoint URL to the price service. Example: https://website/example/
41
+ * @param config - Optional PriceServiceConnectionConfig for custom configurations.
41
42
  */
42
43
  constructor(endpoint: string, config?: PriceServiceConnectionConfig);
43
44
  /**
44
45
  * Fetch Latest PriceFeeds of given price ids.
45
46
  * This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price ids)
46
47
  *
47
- * @param priceIds Array of hex-encoded price ids.
48
+ * @param priceIds - Array of hex-encoded price ids.
48
49
  * @returns Array of PriceFeeds
49
50
  */
50
51
  getLatestPriceFeeds(priceIds: HexString[]): Promise<PriceFeed[] | undefined>;
@@ -54,7 +55,7 @@ export declare class PriceServiceConnection {
54
55
  *
55
56
  * This function is coupled to wormhole implemntation.
56
57
  *
57
- * @param priceIds Array of hex-encoded price ids.
58
+ * @param priceIds - Array of hex-encoded price ids.
58
59
  * @returns Array of base64 encoded VAAs.
59
60
  */
60
61
  getLatestVaas(priceIds: HexString[]): Promise<string[]>;
@@ -66,8 +67,8 @@ export declare class PriceServiceConnection {
66
67
  *
67
68
  * This function is coupled to wormhole implemntation.
68
69
  *
69
- * @param priceId Hex-encoded price id.
70
- * @param publishTime Epoch timestamp in seconds.
70
+ * @param priceId - Hex-encoded price id.
71
+ * @param publishTime - Epoch timestamp in seconds.
71
72
  * @returns Tuple of VAA and publishTime.
72
73
  */
73
74
  getVaa(priceId: HexString, publishTime: EpochTimeStamp): Promise<[string, EpochTimeStamp]>;
@@ -77,8 +78,8 @@ export declare class PriceServiceConnection {
77
78
  * is old and the price service endpoint does not have a db backend for historical requests.
78
79
  * This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price id)
79
80
  *
80
- * @param priceId Hex-encoded price id.
81
- * @param publishTime Epoch timestamp in seconds.
81
+ * @param priceId - Hex-encoded price id.
82
+ * @param publishTime - Epoch timestamp in seconds.
82
83
  * @returns PriceFeed
83
84
  */
84
85
  getPriceFeed(priceId: HexString, publishTime: EpochTimeStamp): Promise<PriceFeed>;
@@ -97,8 +98,8 @@ export declare class PriceServiceConnection {
97
98
  * it calls `connection.onWsError`. If you want to handle the errors you should set the
98
99
  * `onWsError` function to your custom error handler.
99
100
  *
100
- * @param priceIds Array of hex-encoded price ids.
101
- * @param cb Callback function that is called with a PriceFeed upon updates to given price ids.
101
+ * @param priceIds - Array of hex-encoded price ids.
102
+ * @param cb - Callback function that is called with a PriceFeed upon updates to given price ids.
102
103
  */
103
104
  subscribePriceFeedUpdates(priceIds: HexString[], cb: PriceFeedUpdateCallback): Promise<void>;
104
105
  /**
@@ -109,8 +110,8 @@ export declare class PriceServiceConnection {
109
110
  * it calls `connection.onWsError`. If you want to handle the errors you should set the
110
111
  * `onWsError` function to your custom error handler.
111
112
  *
112
- * @param priceIds Array of hex-encoded price ids.
113
- * @param cb Optional callback, if set it will only unsubscribe this callback from updates for given price ids.
113
+ * @param priceIds - Array of hex-encoded price ids.
114
+ * @param cb - Optional callback, if set it will only unsubscribe this callback from updates for given price ids.
114
115
  */
115
116
  unsubscribePriceFeedUpdates(priceIds: HexString[], cb?: PriceFeedUpdateCallback): Promise<void>;
116
117
  /**
@@ -128,4 +129,3 @@ export declare class PriceServiceConnection {
128
129
  */
129
130
  closeWebSocket(): void;
130
131
  }
131
- //# sourceMappingURL=PriceServiceConnection.d.ts.map
@@ -1,21 +1,20 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.ResilientWebSocket = void 0;
7
- const isomorphic_ws_1 = __importDefault(require("isomorphic-ws"));
8
- const PING_TIMEOUT_DURATION = 30000 + 3000; // It is 30s on the server and 3s is added for delays
9
- /**
10
- * This class wraps websocket to provide a resilient web socket client.
11
- *
12
- * It will reconnect if connection fails with exponential backoff. Also, in node, it will reconnect
13
- * if it receives no ping request from server within a while as indication of timeout (assuming
14
- * the server sends it regularly).
15
- *
16
- * This class also logs events if logger is given and by replacing onError method you can handle
17
- * connection errors yourself (e.g: do not retry and close the connection).
18
- */
1
+ /* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-misused-promises */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-unnecessary-condition */ /* eslint-disable unicorn/prefer-add-event-listener */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/no-empty-function */ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "ResilientWebSocket", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return ResilientWebSocket;
9
+ }
10
+ });
11
+ const _isomorphicws = /*#__PURE__*/ _interop_require_default(require("isomorphic-ws"));
12
+ function _interop_require_default(obj) {
13
+ return obj && obj.__esModule ? obj : {
14
+ default: obj
15
+ };
16
+ }
17
+ const PING_TIMEOUT_DURATION = 30_000 + 3000; // It is 30s on the server and 3s is added for delays
19
18
  class ResilientWebSocket {
20
19
  endpoint;
21
20
  wsClient;
@@ -26,25 +25,24 @@ class ResilientWebSocket {
26
25
  onError;
27
26
  onMessage;
28
27
  onReconnect;
29
- constructor(endpoint, logger) {
28
+ constructor(endpoint, logger){
30
29
  this.endpoint = endpoint;
31
30
  this.logger = logger;
32
31
  this.wsFailedAttempts = 0;
33
- this.onError = (error) => {
32
+ this.onError = (error)=>{
34
33
  this.logger?.error(error);
35
34
  };
36
35
  this.wsUserClosed = true;
37
- this.onMessage = () => { };
38
- this.onReconnect = () => { };
36
+ this.onMessage = ()=>{};
37
+ this.onReconnect = ()=>{};
39
38
  }
40
39
  async send(data) {
41
40
  this.logger?.info(`Sending ${data}`);
42
41
  await this.waitForMaybeReadyWebSocket();
43
42
  if (this.wsClient === undefined) {
44
43
  this.logger?.error("Couldn't connect to the websocket server. Error callback is called.");
45
- }
46
- else {
47
- this.wsClient?.send(data);
44
+ } else {
45
+ this.wsClient.send(data);
48
46
  }
49
47
  }
50
48
  async startWebSocket() {
@@ -52,26 +50,28 @@ class ResilientWebSocket {
52
50
  return;
53
51
  }
54
52
  this.logger?.info(`Creating Web Socket client`);
55
- this.wsClient = new isomorphic_ws_1.default(this.endpoint);
53
+ this.wsClient = new _isomorphicws.default(this.endpoint);
56
54
  this.wsUserClosed = false;
57
- this.wsClient.onopen = () => {
55
+ this.wsClient.addEventListener("open", ()=>{
58
56
  this.wsFailedAttempts = 0;
59
57
  // Ping handler is undefined in browser side so heartbeat is disabled.
60
58
  if (this.wsClient.on !== undefined) {
61
59
  this.heartbeat();
62
60
  }
63
- };
64
- this.wsClient.onerror = (event) => {
61
+ });
62
+ this.wsClient.onerror = (event)=>{
65
63
  this.onError(event.error);
66
64
  };
67
- this.wsClient.onmessage = (event) => {
65
+ this.wsClient.onmessage = (event)=>{
68
66
  this.onMessage(event.data);
69
67
  };
70
- this.wsClient.onclose = async () => {
68
+ this.wsClient.addEventListener("close", async ()=>{
71
69
  if (this.pingTimeout !== undefined) {
72
70
  clearInterval(this.pingTimeout);
73
71
  }
74
- if (this.wsUserClosed === false) {
72
+ if (this.wsUserClosed) {
73
+ this.logger?.info("The connection has been closed successfully.");
74
+ } else {
75
75
  this.wsFailedAttempts += 1;
76
76
  this.wsClient = undefined;
77
77
  const waitTime = expoBackoff(this.wsFailedAttempts);
@@ -79,29 +79,25 @@ class ResilientWebSocket {
79
79
  await sleep(waitTime);
80
80
  this.restartUnexpectedClosedWebsocket();
81
81
  }
82
- else {
83
- this.logger?.info("The connection has been closed successfully.");
84
- }
85
- };
82
+ });
86
83
  if (this.wsClient.on !== undefined) {
87
84
  // Ping handler is undefined in browser side
88
85
  this.wsClient.on("ping", this.heartbeat.bind(this));
89
86
  }
90
87
  }
91
88
  /**
92
- * Heartbeat is only enabled in node clients because they support handling
93
- * ping-pong events.
94
- *
95
- * This approach only works when server constantly pings the clients which.
96
- * Otherwise you might consider sending ping and acting on pong responses
97
- * yourself.
98
- */
99
- heartbeat() {
89
+ * Heartbeat is only enabled in node clients because they support handling
90
+ * ping-pong events.
91
+ *
92
+ * This approach only works when server constantly pings the clients which.
93
+ * Otherwise you might consider sending ping and acting on pong responses
94
+ * yourself.
95
+ */ heartbeat() {
100
96
  this.logger?.info("Heartbeat");
101
97
  if (this.pingTimeout !== undefined) {
102
98
  clearTimeout(this.pingTimeout);
103
99
  }
104
- this.pingTimeout = setTimeout(() => {
100
+ this.pingTimeout = setTimeout(()=>{
105
101
  this.logger?.warn(`Connection timed out. Reconnecting...`);
106
102
  this.wsClient?.terminate();
107
103
  this.restartUnexpectedClosedWebsocket();
@@ -109,20 +105,18 @@ class ResilientWebSocket {
109
105
  }
110
106
  async waitForMaybeReadyWebSocket() {
111
107
  let waitedTime = 0;
112
- while (this.wsClient !== undefined &&
113
- this.wsClient.readyState !== this.wsClient.OPEN) {
108
+ while(this.wsClient !== undefined && this.wsClient.readyState !== this.wsClient.OPEN){
114
109
  if (waitedTime > 5000) {
115
110
  this.wsClient.close();
116
111
  return;
117
- }
118
- else {
112
+ } else {
119
113
  waitedTime += 10;
120
114
  await sleep(10);
121
115
  }
122
116
  }
123
117
  }
124
118
  async restartUnexpectedClosedWebsocket() {
125
- if (this.wsUserClosed === true) {
119
+ if (this.wsUserClosed) {
126
120
  return;
127
121
  }
128
122
  await this.startWebSocket();
@@ -142,9 +136,8 @@ class ResilientWebSocket {
142
136
  this.wsUserClosed = true;
143
137
  }
144
138
  }
145
- exports.ResilientWebSocket = ResilientWebSocket;
146
139
  async function sleep(ms) {
147
- return new Promise((resolve) => setTimeout(resolve, ms));
140
+ return new Promise((resolve)=>setTimeout(resolve, ms));
148
141
  }
149
142
  function expoBackoff(attempts) {
150
143
  return 2 ** attempts * 100;
@@ -1,6 +1,5 @@
1
- /// <reference types="ws" />
2
1
  import WebSocket from "isomorphic-ws";
3
- import { Logger } from "ts-log";
2
+ import type { Logger } from "ts-log";
4
3
  /**
5
4
  * This class wraps websocket to provide a resilient web socket client.
6
5
  *
@@ -22,7 +21,7 @@ export declare class ResilientWebSocket {
22
21
  onMessage: (data: WebSocket.Data) => void;
23
22
  onReconnect: () => void;
24
23
  constructor(endpoint: string, logger?: Logger);
25
- send(data: any): Promise<void>;
24
+ send(data: unknown): Promise<void>;
26
25
  startWebSocket(): Promise<void>;
27
26
  /**
28
27
  * Heartbeat is only enabled in node clients because they support handling
@@ -37,4 +36,3 @@ export declare class ResilientWebSocket {
37
36
  private restartUnexpectedClosedWebsocket;
38
37
  closeWebSocket(): void;
39
38
  }
40
- //# sourceMappingURL=ResillientWebSocket.d.ts.map
package/dist/index.cjs ADDED
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get Price () {
13
+ return _priceservicesdk.Price;
14
+ },
15
+ get PriceFeed () {
16
+ return _priceservicesdk.PriceFeed;
17
+ },
18
+ get PriceFeedMetadata () {
19
+ return _priceservicesdk.PriceFeedMetadata;
20
+ },
21
+ get PriceServiceConnection () {
22
+ return _PriceServiceConnection.PriceServiceConnection;
23
+ },
24
+ get isAccumulatorUpdateData () {
25
+ return _priceservicesdk.isAccumulatorUpdateData;
26
+ },
27
+ get parseAccumulatorUpdateData () {
28
+ return _priceservicesdk.parseAccumulatorUpdateData;
29
+ }
30
+ });
31
+ const _PriceServiceConnection = require("./PriceServiceConnection.cjs");
32
+ const _priceservicesdk = require("@pythnetwork/price-service-sdk");
@@ -0,0 +1,2 @@
1
+ export { type DurationInMs, PriceServiceConnection, type PriceServiceConnectionConfig, } from "./PriceServiceConnection.js";
2
+ export { type HexString, PriceFeedMetadata, PriceFeed, Price, type UnixTimestamp, isAccumulatorUpdateData, parseAccumulatorUpdateData, type AccumulatorUpdateData, } from "@pythnetwork/price-service-sdk";
@@ -0,0 +1 @@
1
+ { "type": "commonjs" }
package/dist/utils.cjs ADDED
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get makeWebsocketUrl () {
13
+ return makeWebsocketUrl;
14
+ },
15
+ get removeLeading0xIfExists () {
16
+ return removeLeading0xIfExists;
17
+ }
18
+ });
19
+ function makeWebsocketUrl(endpoint) {
20
+ const url = new URL("ws", endpoint);
21
+ const useHttps = url.protocol === "https:";
22
+ url.protocol = useHttps ? "wss:" : "ws:";
23
+ return url.toString();
24
+ }
25
+ function removeLeading0xIfExists(id) {
26
+ return id.startsWith("0x") ? id.slice(2) : id;
27
+ }
@@ -1,10 +1,9 @@
1
- import { HexString } from "@pythnetwork/price-service-sdk";
1
+ import type { HexString } from "@pythnetwork/price-service-sdk";
2
2
  /**
3
3
  * Convert http(s) endpoint to ws(s) endpoint.
4
4
  *
5
- * @param endpoint Http(s) protocol endpoint
5
+ * @param endpoint - Http(s) protocol endpoint
6
6
  * @returns Ws(s) protocol endpoint of the same address
7
7
  */
8
8
  export declare function makeWebsocketUrl(endpoint: string): string;
9
9
  export declare function removeLeading0xIfExists(id: HexString): HexString;
10
- //# sourceMappingURL=utils.d.ts.map
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@pythnetwork/price-service-client",
3
- "version": "1.9.1",
3
+ "version": "1.10.0",
4
4
  "description": "Pyth price service client",
5
5
  "deprecated": "This package is deprecated and is no longer maintained. Please use @pythnetwork/hermes-client instead.",
6
6
  "author": {
7
7
  "name": "Pyth Data Association"
8
8
  },
9
9
  "homepage": "https://pyth.network",
10
- "main": "lib/index.js",
11
- "types": "lib/index.d.ts",
10
+ "main": "./dist/index.cjs",
11
+ "types": "./dist/index.d.ts",
12
12
  "files": [
13
- "lib/**/*"
13
+ "dist/**/*"
14
14
  ],
15
15
  "repository": {
16
16
  "type": "git",
@@ -26,15 +26,13 @@
26
26
  ],
27
27
  "license": "Apache-2.0",
28
28
  "devDependencies": {
29
+ "@cprussin/eslint-config": "^4.0.2",
29
30
  "@types/jest": "^29.4.0",
30
31
  "@types/yargs": "^17.0.10",
31
- "@typescript-eslint/eslint-plugin": "^5.21.0",
32
- "@typescript-eslint/parser": "^5.21.0",
33
- "eslint": "^8.14.0",
32
+ "eslint": "^9.23.0",
34
33
  "jest": "^29.4.0",
35
34
  "prettier": "^3.5.3",
36
35
  "ts-jest": "^29.0.5",
37
- "typescript": "^4.6.3",
38
36
  "yargs": "^17.4.1"
39
37
  },
40
38
  "dependencies": {
@@ -44,17 +42,42 @@
44
42
  "isomorphic-ws": "^4.0.1",
45
43
  "ts-log": "^2.2.4",
46
44
  "ws": "^8.6.0",
47
- "@pythnetwork/price-service-sdk": "1.8.0"
45
+ "@pythnetwork/price-service-sdk": "1.9.0"
48
46
  },
47
+ "engines": {
48
+ "node": ">=22.14.0"
49
+ },
50
+ "type": "module",
51
+ "exports": {
52
+ "./PriceServiceConnection": {
53
+ "types": "./dist/PriceServiceConnection.d.ts",
54
+ "default": "./dist/PriceServiceConnection.cjs"
55
+ },
56
+ "./ResillientWebSocket": {
57
+ "types": "./dist/ResillientWebSocket.d.ts",
58
+ "default": "./dist/ResillientWebSocket.cjs"
59
+ },
60
+ ".": {
61
+ "types": "./dist/index.d.ts",
62
+ "default": "./dist/index.cjs"
63
+ },
64
+ "./utils": {
65
+ "types": "./dist/utils.d.ts",
66
+ "default": "./dist/utils.cjs"
67
+ },
68
+ "./package.json": "./package.json"
69
+ },
70
+ "module": "./dist/esm/index.js",
49
71
  "scripts": {
50
72
  "test:e2e": "jest --testPathPattern=.*.e2e.test.ts",
51
- "build": "tsc",
73
+ "build": "ts-duality --noEsm",
52
74
  "example": "pnpm run build && node lib/examples/PriceServiceClient.js",
53
75
  "fix:format": "prettier --write \"src/**/*.ts\"",
54
76
  "fix:lint": "eslint src/ --fix --max-warnings 0",
55
77
  "test:format": "prettier --check \"src/**/*.ts\"",
56
78
  "test:lint": "eslint src/ --max-warnings 0",
57
79
  "preversion": "pnpm run test:lint",
58
- "version": "pnpm run format && git add -A src"
80
+ "version": "pnpm run format && git add -A src",
81
+ "clean": "rm -rf ./dist"
59
82
  }
60
83
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"PriceServiceConnection.d.ts","sourceRoot":"","sources":["../src/PriceServiceConnection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAItE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AAElC,MAAM,MAAM,sBAAsB,GAAG;IAEnC,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IAEzC,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;CACjD,CAAC;AAuBF,MAAM,MAAM,uBAAuB,GAAG,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;AAErE,qBAAa,sBAAsB;IACjC,OAAO,CAAC,UAAU,CAAgB;IAElC,OAAO,CAAC,kBAAkB,CAA+C;IACzE,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,UAAU,CAAqB;IAEvC,OAAO,CAAC,MAAM,CAAS;IAEvB,OAAO,CAAC,sBAAsB,CAAyB;IAEvD;;;;OAIG;IACH,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAElC;;;;;OAKG;gBACS,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,4BAA4B;IA+CnE;;;;;;OAMG;IACG,mBAAmB,CACvB,QAAQ,EAAE,SAAS,EAAE,GACpB,OAAO,CAAC,SAAS,EAAE,GAAG,SAAS,CAAC;IAkBnC;;;;;;;;OAQG;IACG,aAAa,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAS7D;;;;;;;;;;;OAWG;IACG,MAAM,CACV,OAAO,EAAE,SAAS,EAClB,WAAW,EAAE,cAAc,GAC1B,OAAO,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAUpC;;;;;;;;;OASG;IACG,YAAY,CAChB,OAAO,EAAE,SAAS,EAClB,WAAW,EAAE,cAAc,GAC1B,OAAO,CAAC,SAAS,CAAC;IAarB;;;;;OAKG;IACG,eAAe,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAK7C;;;;;;;;;;OAUG;IACG,yBAAyB,CAC7B,QAAQ,EAAE,SAAS,EAAE,EACrB,EAAE,EAAE,uBAAuB;IA8B7B;;;;;;;;;;OAUG;IACG,2BAA2B,CAC/B,QAAQ,EAAE,SAAS,EAAE,EACrB,EAAE,CAAC,EAAE,uBAAuB;IA4C9B;;;;OAIG;IACG,cAAc;IAyEpB;;;;;;OAMG;IACH,cAAc;CAKf"}
@@ -1,314 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.PriceServiceConnection = void 0;
7
- const price_service_sdk_1 = require("@pythnetwork/price-service-sdk");
8
- const axios_1 = __importDefault(require("axios"));
9
- const axios_retry_1 = __importDefault(require("axios-retry"));
10
- const ResillientWebSocket_1 = require("./ResillientWebSocket");
11
- const utils_1 = require("./utils");
12
- class PriceServiceConnection {
13
- httpClient;
14
- priceFeedCallbacks;
15
- wsClient;
16
- wsEndpoint;
17
- logger;
18
- priceFeedRequestConfig;
19
- /**
20
- * Custom handler for web socket errors (connection and message parsing).
21
- *
22
- * Default handler only logs the errors.
23
- */
24
- onWsError;
25
- /**
26
- * Constructs a new Connection.
27
- *
28
- * @param endpoint endpoint URL to the price service. Example: https://website/example/
29
- * @param config Optional PriceServiceConnectionConfig for custom configurations.
30
- */
31
- constructor(endpoint, config) {
32
- this.httpClient = axios_1.default.create({
33
- baseURL: endpoint,
34
- timeout: config?.timeout || 5000,
35
- });
36
- (0, axios_retry_1.default)(this.httpClient, {
37
- retries: config?.httpRetries || 3,
38
- retryDelay: axios_retry_1.default.exponentialDelay,
39
- });
40
- this.priceFeedRequestConfig = {
41
- binary: config?.priceFeedRequestConfig?.binary,
42
- verbose: config?.priceFeedRequestConfig?.verbose ?? config?.verbose,
43
- allowOutOfOrder: config?.priceFeedRequestConfig?.allowOutOfOrder,
44
- };
45
- this.priceFeedCallbacks = new Map();
46
- // Default logger is console for only warnings and errors.
47
- this.logger = config?.logger || {
48
- trace: () => { },
49
- debug: () => { },
50
- info: () => { },
51
- warn: console.warn,
52
- error: console.error,
53
- };
54
- this.onWsError = (error) => {
55
- this.logger.error(error);
56
- // Exit the process if it is running in node.
57
- if (typeof process !== "undefined" &&
58
- typeof process.exit === "function") {
59
- this.logger.error("Halting the process due to the websocket error");
60
- process.exit(1);
61
- }
62
- else {
63
- this.logger.error("Cannot halt process. Please handle the websocket error.");
64
- }
65
- };
66
- this.wsEndpoint = (0, utils_1.makeWebsocketUrl)(endpoint);
67
- }
68
- /**
69
- * Fetch Latest PriceFeeds of given price ids.
70
- * This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price ids)
71
- *
72
- * @param priceIds Array of hex-encoded price ids.
73
- * @returns Array of PriceFeeds
74
- */
75
- async getLatestPriceFeeds(priceIds) {
76
- if (priceIds.length === 0) {
77
- return [];
78
- }
79
- const response = await this.httpClient.get("/api/latest_price_feeds", {
80
- params: {
81
- ids: priceIds,
82
- verbose: this.priceFeedRequestConfig.verbose,
83
- binary: this.priceFeedRequestConfig.binary,
84
- },
85
- });
86
- const priceFeedsJson = response.data;
87
- return priceFeedsJson.map((priceFeedJson) => price_service_sdk_1.PriceFeed.fromJson(priceFeedJson));
88
- }
89
- /**
90
- * Fetch latest VAA of given price ids.
91
- * This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price ids)
92
- *
93
- * This function is coupled to wormhole implemntation.
94
- *
95
- * @param priceIds Array of hex-encoded price ids.
96
- * @returns Array of base64 encoded VAAs.
97
- */
98
- async getLatestVaas(priceIds) {
99
- const response = await this.httpClient.get("/api/latest_vaas", {
100
- params: {
101
- ids: priceIds,
102
- },
103
- });
104
- return response.data;
105
- }
106
- /**
107
- * Fetch the earliest VAA of the given price id that is published since the given publish time.
108
- * This will throw an error if the given publish time is in the future, or if the publish time
109
- * is old and the price service endpoint does not have a db backend for historical requests.
110
- * This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price id)
111
- *
112
- * This function is coupled to wormhole implemntation.
113
- *
114
- * @param priceId Hex-encoded price id.
115
- * @param publishTime Epoch timestamp in seconds.
116
- * @returns Tuple of VAA and publishTime.
117
- */
118
- async getVaa(priceId, publishTime) {
119
- const response = await this.httpClient.get("/api/get_vaa", {
120
- params: {
121
- id: priceId,
122
- publish_time: publishTime,
123
- },
124
- });
125
- return [response.data.vaa, response.data.publishTime];
126
- }
127
- /**
128
- * Fetch the PriceFeed of the given price id that is published since the given publish time.
129
- * This will throw an error if the given publish time is in the future, or if the publish time
130
- * is old and the price service endpoint does not have a db backend for historical requests.
131
- * This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price id)
132
- *
133
- * @param priceId Hex-encoded price id.
134
- * @param publishTime Epoch timestamp in seconds.
135
- * @returns PriceFeed
136
- */
137
- async getPriceFeed(priceId, publishTime) {
138
- const response = await this.httpClient.get("/api/get_price_feed", {
139
- params: {
140
- id: priceId,
141
- publish_time: publishTime,
142
- verbose: this.priceFeedRequestConfig.verbose,
143
- binary: this.priceFeedRequestConfig.binary,
144
- },
145
- });
146
- return price_service_sdk_1.PriceFeed.fromJson(response.data);
147
- }
148
- /**
149
- * Fetch the list of available price feed ids.
150
- * This will throw an axios error if there is a network problem or the price service returns a non-ok response.
151
- *
152
- * @returns Array of hex-encoded price ids.
153
- */
154
- async getPriceFeedIds() {
155
- const response = await this.httpClient.get("/api/price_feed_ids");
156
- return response.data;
157
- }
158
- /**
159
- * Subscribe to updates for given price ids.
160
- *
161
- * It will start a websocket connection if it's not started yet.
162
- * Also, it won't throw any exception if given price ids are invalid or connection errors. Instead,
163
- * it calls `connection.onWsError`. If you want to handle the errors you should set the
164
- * `onWsError` function to your custom error handler.
165
- *
166
- * @param priceIds Array of hex-encoded price ids.
167
- * @param cb Callback function that is called with a PriceFeed upon updates to given price ids.
168
- */
169
- async subscribePriceFeedUpdates(priceIds, cb) {
170
- if (this.wsClient === undefined) {
171
- await this.startWebSocket();
172
- }
173
- priceIds = priceIds.map((priceId) => (0, utils_1.removeLeading0xIfExists)(priceId));
174
- const newPriceIds = [];
175
- for (const id of priceIds) {
176
- if (!this.priceFeedCallbacks.has(id)) {
177
- this.priceFeedCallbacks.set(id, new Set());
178
- newPriceIds.push(id);
179
- }
180
- this.priceFeedCallbacks.get(id).add(cb);
181
- }
182
- const message = {
183
- ids: newPriceIds,
184
- type: "subscribe",
185
- verbose: this.priceFeedRequestConfig.verbose,
186
- binary: this.priceFeedRequestConfig.binary,
187
- allow_out_of_order: this.priceFeedRequestConfig.allowOutOfOrder,
188
- };
189
- await this.wsClient?.send(JSON.stringify(message));
190
- }
191
- /**
192
- * Unsubscribe from updates for given price ids.
193
- *
194
- * It will close the websocket connection if it's not subscribed to any price feed updates anymore.
195
- * Also, it won't throw any exception if given price ids are invalid or connection errors. Instead,
196
- * it calls `connection.onWsError`. If you want to handle the errors you should set the
197
- * `onWsError` function to your custom error handler.
198
- *
199
- * @param priceIds Array of hex-encoded price ids.
200
- * @param cb Optional callback, if set it will only unsubscribe this callback from updates for given price ids.
201
- */
202
- async unsubscribePriceFeedUpdates(priceIds, cb) {
203
- if (this.wsClient === undefined) {
204
- await this.startWebSocket();
205
- }
206
- priceIds = priceIds.map((priceId) => (0, utils_1.removeLeading0xIfExists)(priceId));
207
- const removedPriceIds = [];
208
- for (const id of priceIds) {
209
- if (this.priceFeedCallbacks.has(id)) {
210
- let idRemoved = false;
211
- if (cb === undefined) {
212
- this.priceFeedCallbacks.delete(id);
213
- idRemoved = true;
214
- }
215
- else {
216
- this.priceFeedCallbacks.get(id).delete(cb);
217
- if (this.priceFeedCallbacks.get(id).size === 0) {
218
- this.priceFeedCallbacks.delete(id);
219
- idRemoved = true;
220
- }
221
- }
222
- if (idRemoved) {
223
- removedPriceIds.push(id);
224
- }
225
- }
226
- }
227
- const message = {
228
- ids: removedPriceIds,
229
- type: "unsubscribe",
230
- };
231
- await this.wsClient?.send(JSON.stringify(message));
232
- if (this.priceFeedCallbacks.size === 0) {
233
- this.closeWebSocket();
234
- }
235
- }
236
- /**
237
- * Starts connection websocket.
238
- *
239
- * This function is called automatically upon subscribing to price feed updates.
240
- */
241
- async startWebSocket() {
242
- if (this.wsEndpoint === undefined) {
243
- throw new Error("Websocket endpoint is undefined.");
244
- }
245
- this.wsClient = new ResillientWebSocket_1.ResilientWebSocket(this.wsEndpoint, this.logger);
246
- this.wsClient.onError = this.onWsError;
247
- this.wsClient.onReconnect = () => {
248
- if (this.priceFeedCallbacks.size > 0) {
249
- const message = {
250
- ids: Array.from(this.priceFeedCallbacks.keys()),
251
- type: "subscribe",
252
- verbose: this.priceFeedRequestConfig.verbose,
253
- binary: this.priceFeedRequestConfig.binary,
254
- allow_out_of_order: this.priceFeedRequestConfig.allowOutOfOrder,
255
- };
256
- this.logger.info("Resubscribing to existing price feeds.");
257
- this.wsClient?.send(JSON.stringify(message));
258
- }
259
- };
260
- this.wsClient.onMessage = (data) => {
261
- this.logger.info(`Received message ${data.toString()}`);
262
- let message;
263
- try {
264
- message = JSON.parse(data.toString());
265
- }
266
- catch (e) {
267
- this.logger.error(`Error parsing message ${data.toString()} as JSON.`);
268
- this.logger.error(e);
269
- this.onWsError(e);
270
- return;
271
- }
272
- if (message.type === "response") {
273
- if (message.status === "error") {
274
- this.logger.error(`Error response from the websocket server ${message.error}.`);
275
- this.onWsError(new Error(message.error));
276
- }
277
- }
278
- else if (message.type === "price_update") {
279
- let priceFeed;
280
- try {
281
- priceFeed = price_service_sdk_1.PriceFeed.fromJson(message.price_feed);
282
- }
283
- catch (e) {
284
- this.logger.error(`Error parsing price feeds from message ${data.toString()}.`);
285
- this.logger.error(e);
286
- this.onWsError(e);
287
- return;
288
- }
289
- if (this.priceFeedCallbacks.has(priceFeed.id)) {
290
- for (const cb of this.priceFeedCallbacks.get(priceFeed.id)) {
291
- cb(priceFeed);
292
- }
293
- }
294
- }
295
- else {
296
- this.logger.warn(`Ignoring unsupported server response ${data.toString()}.`);
297
- }
298
- };
299
- await this.wsClient.startWebSocket();
300
- }
301
- /**
302
- * Closes connection websocket.
303
- *
304
- * At termination, the websocket should be closed to finish the
305
- * process elegantly. It will automatically close when the connection
306
- * is subscribed to no price feeds.
307
- */
308
- closeWebSocket() {
309
- this.wsClient?.closeWebSocket();
310
- this.wsClient = undefined;
311
- this.priceFeedCallbacks.clear();
312
- }
313
- }
314
- exports.PriceServiceConnection = PriceServiceConnection;
@@ -1 +0,0 @@
1
- {"version":3,"file":"ResillientWebSocket.d.ts","sourceRoot":"","sources":["../src/ResillientWebSocket.ts"],"names":[],"mappings":";AAAA,OAAO,SAAS,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC;;;;;;;;;GASG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,MAAM,CAAqB;IAEnC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAChC,SAAS,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC;IAC1C,WAAW,EAAE,MAAM,IAAI,CAAC;gBAEZ,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;IAavC,IAAI,CAAC,IAAI,EAAE,GAAG;IAcd,cAAc;IAqDpB;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS;YAcH,0BAA0B;YAgB1B,gCAAgC;IAkB9C,cAAc;CAQf"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=PriceServiceClient.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PriceServiceClient.d.ts","sourceRoot":"","sources":["../../src/examples/PriceServiceClient.ts"],"names":[],"mappings":""}
@@ -1,53 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const yargs_1 = __importDefault(require("yargs"));
7
- const helpers_1 = require("yargs/helpers");
8
- const index_1 = require("../index");
9
- function sleep(ms) {
10
- return new Promise((resolve) => setTimeout(resolve, ms));
11
- }
12
- const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
13
- .option("endpoint", {
14
- description: "Endpoint URL for the price service. e.g: https://endpoint/example",
15
- type: "string",
16
- required: true,
17
- })
18
- .option("price-ids", {
19
- description: "Space separated price feed ids (in hex without leading 0x) to fetch." +
20
- " e.g: f9c0172ba10dfa4d19088d...",
21
- type: "array",
22
- required: true,
23
- })
24
- .help()
25
- .alias("help", "h")
26
- .parserConfiguration({
27
- "parse-numbers": false,
28
- })
29
- .parseSync();
30
- async function run() {
31
- const connection = new index_1.PriceServiceConnection(argv.endpoint, {
32
- logger: console,
33
- priceFeedRequestConfig: {
34
- binary: true,
35
- },
36
- });
37
- const priceIds = argv.priceIds;
38
- const priceFeeds = await connection.getLatestPriceFeeds(priceIds);
39
- console.log(priceFeeds);
40
- console.log(priceFeeds?.at(0)?.getPriceNoOlderThan(60));
41
- console.log("Subscribing to price feed updates.");
42
- await connection.subscribePriceFeedUpdates(priceIds, (priceFeed) => {
43
- console.log(`Current price for ${priceFeed.id}: ${JSON.stringify(priceFeed.getPriceNoOlderThan(60))}.`);
44
- console.log(priceFeed.getVAA());
45
- });
46
- await sleep(600000);
47
- // To close the websocket you should either unsubscribe from all
48
- // price feeds or call `connection.stopWebSocket()` directly.
49
- console.log("Unsubscribing from price feed updates.");
50
- await connection.unsubscribePriceFeedUpdates(priceIds);
51
- // connection.closeWebSocket();
52
- }
53
- run();
package/lib/index.d.ts DELETED
@@ -1,3 +0,0 @@
1
- export { DurationInMs, PriceServiceConnection, PriceServiceConnectionConfig, } from "./PriceServiceConnection";
2
- export { HexString, PriceFeedMetadata, PriceFeed, Price, UnixTimestamp, isAccumulatorUpdateData, parseAccumulatorUpdateData, AccumulatorUpdateData, } from "@pythnetwork/price-service-sdk";
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,4BAA4B,GAC7B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,SAAS,EACT,KAAK,EACL,aAAa,EACb,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,gCAAgC,CAAC"}
package/lib/index.js DELETED
@@ -1,11 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseAccumulatorUpdateData = exports.isAccumulatorUpdateData = exports.Price = exports.PriceFeed = exports.PriceFeedMetadata = exports.PriceServiceConnection = void 0;
4
- var PriceServiceConnection_1 = require("./PriceServiceConnection");
5
- Object.defineProperty(exports, "PriceServiceConnection", { enumerable: true, get: function () { return PriceServiceConnection_1.PriceServiceConnection; } });
6
- var price_service_sdk_1 = require("@pythnetwork/price-service-sdk");
7
- Object.defineProperty(exports, "PriceFeedMetadata", { enumerable: true, get: function () { return price_service_sdk_1.PriceFeedMetadata; } });
8
- Object.defineProperty(exports, "PriceFeed", { enumerable: true, get: function () { return price_service_sdk_1.PriceFeed; } });
9
- Object.defineProperty(exports, "Price", { enumerable: true, get: function () { return price_service_sdk_1.Price; } });
10
- Object.defineProperty(exports, "isAccumulatorUpdateData", { enumerable: true, get: function () { return price_service_sdk_1.isAccumulatorUpdateData; } });
11
- Object.defineProperty(exports, "parseAccumulatorUpdateData", { enumerable: true, get: function () { return price_service_sdk_1.parseAccumulatorUpdateData; } });
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAE3D;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,UAOhD;AAED,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,SAAS,GAAG,SAAS,CAMhE"}
package/lib/utils.js DELETED
@@ -1,25 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.removeLeading0xIfExists = exports.makeWebsocketUrl = void 0;
4
- /**
5
- * Convert http(s) endpoint to ws(s) endpoint.
6
- *
7
- * @param endpoint Http(s) protocol endpoint
8
- * @returns Ws(s) protocol endpoint of the same address
9
- */
10
- function makeWebsocketUrl(endpoint) {
11
- const url = new URL("ws", endpoint);
12
- const useHttps = url.protocol === "https:";
13
- url.protocol = useHttps ? "wss:" : "ws:";
14
- return url.toString();
15
- }
16
- exports.makeWebsocketUrl = makeWebsocketUrl;
17
- function removeLeading0xIfExists(id) {
18
- if (id.startsWith("0x")) {
19
- return id.substring(2);
20
- }
21
- else {
22
- return id;
23
- }
24
- }
25
- exports.removeLeading0xIfExists = removeLeading0xIfExists;