@pythnetwork/pyth-lazer-sdk 4.0.0 → 5.2.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.
Files changed (45) hide show
  1. package/README.md +57 -0
  2. package/dist/cjs/client.cjs +229 -0
  3. package/dist/cjs/constants.cjs +36 -0
  4. package/dist/cjs/index.cjs +20 -0
  5. package/dist/cjs/package.json +1 -1
  6. package/dist/cjs/protocol.cjs +33 -0
  7. package/dist/cjs/protocol.d.ts +4 -1
  8. package/dist/cjs/socket/{resilient-websocket.js → resilient-websocket.cjs} +60 -45
  9. package/dist/cjs/socket/resilient-websocket.d.ts +15 -1
  10. package/dist/cjs/socket/{websocket-pool.js → websocket-pool.cjs} +77 -64
  11. package/dist/cjs/socket/websocket-pool.d.ts +5 -2
  12. package/dist/cjs/util/buffer-util.cjs +33 -0
  13. package/dist/cjs/util/buffer-util.d.ts +7 -0
  14. package/dist/cjs/util/env-util.cjs +33 -0
  15. package/dist/cjs/util/env-util.d.ts +17 -0
  16. package/dist/cjs/util/index.cjs +20 -0
  17. package/dist/cjs/util/index.d.ts +3 -0
  18. package/dist/cjs/util/url-util.cjs +17 -0
  19. package/dist/cjs/util/url-util.d.ts +8 -0
  20. package/dist/esm/client.mjs +219 -0
  21. package/dist/esm/index.mjs +3 -0
  22. package/dist/esm/package.json +1 -1
  23. package/dist/esm/protocol.d.ts +4 -1
  24. package/dist/esm/protocol.mjs +12 -0
  25. package/dist/esm/socket/resilient-websocket.d.ts +15 -1
  26. package/dist/esm/socket/{resilient-websocket.js → resilient-websocket.mjs} +42 -35
  27. package/dist/esm/socket/websocket-pool.d.ts +5 -2
  28. package/dist/esm/socket/{websocket-pool.js → websocket-pool.mjs} +58 -54
  29. package/dist/esm/util/buffer-util.d.ts +7 -0
  30. package/dist/esm/util/buffer-util.mjs +27 -0
  31. package/dist/esm/util/env-util.d.ts +17 -0
  32. package/dist/esm/util/env-util.mjs +23 -0
  33. package/dist/esm/util/index.d.ts +3 -0
  34. package/dist/esm/util/index.mjs +3 -0
  35. package/dist/esm/util/url-util.d.ts +8 -0
  36. package/dist/esm/util/url-util.mjs +13 -0
  37. package/package.json +111 -15
  38. package/dist/cjs/client.js +0 -236
  39. package/dist/cjs/constants.js +0 -9
  40. package/dist/cjs/index.js +0 -19
  41. package/dist/cjs/protocol.js +0 -11
  42. package/dist/esm/client.js +0 -230
  43. package/dist/esm/index.js +0 -3
  44. package/dist/esm/protocol.js +0 -8
  45. /package/dist/esm/{constants.js → constants.mjs} +0 -0
package/package.json CHANGED
@@ -1,24 +1,120 @@
1
1
  {
2
2
  "name": "@pythnetwork/pyth-lazer-sdk",
3
- "version": "4.0.0",
3
+ "version": "5.2.0",
4
4
  "description": "Pyth Lazer SDK",
5
+ "engines": {
6
+ "node": ">=22.14.0"
7
+ },
5
8
  "publishConfig": {
6
9
  "access": "public"
7
10
  },
8
11
  "files": [
9
12
  "dist/**/*"
10
13
  ],
11
- "main": "./dist/cjs/index.js",
14
+ "main": "./dist/cjs/index.cjs",
12
15
  "types": "./dist/cjs/index.d.ts",
13
16
  "exports": {
14
- "import": {
15
- "types": "./dist/esm/index.d.ts",
16
- "default": "./dist/esm/index.js"
17
+ "./client": {
18
+ "require": {
19
+ "types": "./dist/cjs/client.d.ts",
20
+ "default": "./dist/cjs/client.cjs"
21
+ },
22
+ "import": {
23
+ "types": "./dist/esm/client.d.ts",
24
+ "default": "./dist/esm/client.mjs"
25
+ }
26
+ },
27
+ "./constants": {
28
+ "require": {
29
+ "types": "./dist/cjs/constants.d.ts",
30
+ "default": "./dist/cjs/constants.cjs"
31
+ },
32
+ "import": {
33
+ "types": "./dist/esm/constants.d.ts",
34
+ "default": "./dist/esm/constants.mjs"
35
+ }
36
+ },
37
+ ".": {
38
+ "require": {
39
+ "types": "./dist/cjs/index.d.ts",
40
+ "default": "./dist/cjs/index.cjs"
41
+ },
42
+ "import": {
43
+ "types": "./dist/esm/index.d.ts",
44
+ "default": "./dist/esm/index.mjs"
45
+ }
46
+ },
47
+ "./protocol": {
48
+ "require": {
49
+ "types": "./dist/cjs/protocol.d.ts",
50
+ "default": "./dist/cjs/protocol.cjs"
51
+ },
52
+ "import": {
53
+ "types": "./dist/esm/protocol.d.ts",
54
+ "default": "./dist/esm/protocol.mjs"
55
+ }
56
+ },
57
+ "./socket/resilient-websocket": {
58
+ "require": {
59
+ "types": "./dist/cjs/socket/resilient-websocket.d.ts",
60
+ "default": "./dist/cjs/socket/resilient-websocket.cjs"
61
+ },
62
+ "import": {
63
+ "types": "./dist/esm/socket/resilient-websocket.d.ts",
64
+ "default": "./dist/esm/socket/resilient-websocket.mjs"
65
+ }
66
+ },
67
+ "./socket/websocket-pool": {
68
+ "require": {
69
+ "types": "./dist/cjs/socket/websocket-pool.d.ts",
70
+ "default": "./dist/cjs/socket/websocket-pool.cjs"
71
+ },
72
+ "import": {
73
+ "types": "./dist/esm/socket/websocket-pool.d.ts",
74
+ "default": "./dist/esm/socket/websocket-pool.mjs"
75
+ }
76
+ },
77
+ "./util/buffer-util": {
78
+ "require": {
79
+ "types": "./dist/cjs/util/buffer-util.d.ts",
80
+ "default": "./dist/cjs/util/buffer-util.cjs"
81
+ },
82
+ "import": {
83
+ "types": "./dist/esm/util/buffer-util.d.ts",
84
+ "default": "./dist/esm/util/buffer-util.mjs"
85
+ }
86
+ },
87
+ "./util/env-util": {
88
+ "require": {
89
+ "types": "./dist/cjs/util/env-util.d.ts",
90
+ "default": "./dist/cjs/util/env-util.cjs"
91
+ },
92
+ "import": {
93
+ "types": "./dist/esm/util/env-util.d.ts",
94
+ "default": "./dist/esm/util/env-util.mjs"
95
+ }
96
+ },
97
+ "./util": {
98
+ "require": {
99
+ "types": "./dist/cjs/util/index.d.ts",
100
+ "default": "./dist/cjs/util/index.cjs"
101
+ },
102
+ "import": {
103
+ "types": "./dist/esm/util/index.d.ts",
104
+ "default": "./dist/esm/util/index.mjs"
105
+ }
106
+ },
107
+ "./util/url-util": {
108
+ "require": {
109
+ "types": "./dist/cjs/util/url-util.d.ts",
110
+ "default": "./dist/cjs/util/url-util.cjs"
111
+ },
112
+ "import": {
113
+ "types": "./dist/esm/util/url-util.d.ts",
114
+ "default": "./dist/esm/util/url-util.mjs"
115
+ }
17
116
  },
18
- "require": {
19
- "types": "./dist/cjs/index.d.ts",
20
- "default": "./dist/cjs/index.js"
21
- }
117
+ "./package.json": "./package.json"
22
118
  },
23
119
  "devDependencies": {
24
120
  "@cprussin/eslint-config": "^4.0.2",
@@ -28,8 +124,7 @@
28
124
  "eslint": "^9.23.0",
29
125
  "prettier": "^3.5.3",
30
126
  "ts-node": "^10.9.2",
31
- "typedoc": "^0.26.8",
32
- "typescript": "^5.8.2"
127
+ "typedoc": "^0.26.8"
33
128
  },
34
129
  "bugs": {
35
130
  "url": "https://github.com/pyth-network/pyth-crosschain/issues"
@@ -48,14 +143,14 @@
48
143
  "license": "Apache-2.0",
49
144
  "dependencies": {
50
145
  "@isaacs/ttlcache": "^1.4.1",
51
- "cross-fetch": "^4.0.0",
146
+ "buffer": "^6.0.3",
52
147
  "isomorphic-ws": "^5.0.0",
53
148
  "ts-log": "^2.2.7",
54
149
  "ws": "^8.18.0"
55
150
  },
151
+ "module": "./dist/esm/index.mjs",
56
152
  "scripts": {
57
- "build:cjs": "tsc --project tsconfig.build.json --verbatimModuleSyntax false --module commonjs --outDir ./dist/cjs && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
58
- "build:esm": "tsc --project tsconfig.build.json --outDir ./dist/esm && echo '{\"type\":\"module\"}' > dist/esm/package.json",
153
+ "build": "ts-duality --clean",
59
154
  "fix:lint": "eslint --fix . --max-warnings 0",
60
155
  "test:lint": "eslint . --max-warnings 0",
61
156
  "test:types": "tsc",
@@ -64,6 +159,7 @@
64
159
  "example:streaming": "node --loader ts-node/esm examples/streaming.js",
65
160
  "example:history": "node --loader ts-node/esm examples/history.js",
66
161
  "example:symbols": "node --loader ts-node/esm examples/symbols.js",
67
- "doc": "typedoc --out docs/typedoc src"
162
+ "doc": "typedoc --out docs/typedoc src",
163
+ "clean": "rm -rf ./dist"
68
164
  }
69
165
  }
@@ -1,236 +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.PythLazerClient = void 0;
7
- const cross_fetch_1 = __importDefault(require("cross-fetch"));
8
- const ts_log_1 = require("ts-log");
9
- const constants_js_1 = require("./constants.js");
10
- const protocol_js_1 = require("./protocol.js");
11
- const websocket_pool_js_1 = require("./socket/websocket-pool.js");
12
- const UINT16_NUM_BYTES = 2;
13
- const UINT32_NUM_BYTES = 4;
14
- const UINT64_NUM_BYTES = 8;
15
- class PythLazerClient {
16
- token;
17
- metadataServiceUrl;
18
- priceServiceUrl;
19
- logger;
20
- wsp;
21
- constructor(token, metadataServiceUrl, priceServiceUrl, logger, wsp) {
22
- this.token = token;
23
- this.metadataServiceUrl = metadataServiceUrl;
24
- this.priceServiceUrl = priceServiceUrl;
25
- this.logger = logger;
26
- this.wsp = wsp;
27
- }
28
- /**
29
- * Gets the WebSocket pool. If the WebSocket pool is not configured, an error is thrown.
30
- * @throws Error if WebSocket pool is not configured
31
- * @returns The WebSocket pool
32
- */
33
- getWebSocketPool() {
34
- if (!this.wsp) {
35
- throw new Error("WebSocket pool is not available. Make sure to provide webSocketPoolConfig when creating the client.");
36
- }
37
- return this.wsp;
38
- }
39
- /**
40
- * Creates a new PythLazerClient instance.
41
- * @param config - Configuration including token, metadata service URL, and price service URL, and WebSocket pool configuration
42
- */
43
- static async create(config) {
44
- const token = config.token;
45
- // Collect and remove trailing slash from URLs
46
- const metadataServiceUrl = (config.metadataServiceUrl ?? constants_js_1.DEFAULT_METADATA_SERVICE_URL).replace(/\/+$/, "");
47
- const priceServiceUrl = (config.priceServiceUrl ?? constants_js_1.DEFAULT_PRICE_SERVICE_URL).replace(/\/+$/, "");
48
- const logger = config.logger ?? ts_log_1.dummyLogger;
49
- // If webSocketPoolConfig is provided, create a WebSocket pool and block until at least one connection is established.
50
- let wsp;
51
- if (config.webSocketPoolConfig) {
52
- wsp = await websocket_pool_js_1.WebSocketPool.create(config.webSocketPoolConfig, token, logger);
53
- }
54
- return new PythLazerClient(token, metadataServiceUrl, priceServiceUrl, logger, wsp);
55
- }
56
- /**
57
- * Adds a message listener that receives either JSON or binary responses from the WebSocket connections.
58
- * The listener will be called for each message received, with deduplication across redundant connections.
59
- * @param handler - Callback function that receives the parsed message. The message can be either a JSON response
60
- * or a binary response containing EVM, Solana, or parsed payload data.
61
- */
62
- addMessageListener(handler) {
63
- const wsp = this.getWebSocketPool();
64
- wsp.addMessageListener((data) => {
65
- if (typeof data == "string") {
66
- handler({
67
- type: "json",
68
- value: JSON.parse(data),
69
- });
70
- }
71
- else if (Buffer.isBuffer(data)) {
72
- let pos = 0;
73
- const magic = data.subarray(pos, pos + UINT32_NUM_BYTES).readUint32LE();
74
- pos += UINT32_NUM_BYTES;
75
- if (magic != protocol_js_1.BINARY_UPDATE_FORMAT_MAGIC_LE) {
76
- throw new Error("binary update format magic mismatch");
77
- }
78
- // TODO: some uint64 values may not be representable as Number.
79
- const subscriptionId = Number(data.subarray(pos, pos + UINT64_NUM_BYTES).readBigInt64BE());
80
- pos += UINT64_NUM_BYTES;
81
- const value = { subscriptionId };
82
- while (pos < data.length) {
83
- const len = data.subarray(pos, pos + UINT16_NUM_BYTES).readUint16BE();
84
- pos += UINT16_NUM_BYTES;
85
- const magic = data
86
- .subarray(pos, pos + UINT32_NUM_BYTES)
87
- .readUint32LE();
88
- if (magic == protocol_js_1.FORMAT_MAGICS_LE.EVM) {
89
- value.evm = data.subarray(pos, pos + len);
90
- }
91
- else if (magic == protocol_js_1.FORMAT_MAGICS_LE.SOLANA) {
92
- value.solana = data.subarray(pos, pos + len);
93
- }
94
- else if (magic == protocol_js_1.FORMAT_MAGICS_LE.LE_ECDSA) {
95
- value.leEcdsa = data.subarray(pos, pos + len);
96
- }
97
- else if (magic == protocol_js_1.FORMAT_MAGICS_LE.LE_UNSIGNED) {
98
- value.leUnsigned = data.subarray(pos, pos + len);
99
- }
100
- else if (magic == protocol_js_1.FORMAT_MAGICS_LE.JSON) {
101
- value.parsed = JSON.parse(data.subarray(pos + UINT32_NUM_BYTES, pos + len).toString());
102
- }
103
- else {
104
- throw new Error("unknown magic: " + magic.toString());
105
- }
106
- pos += len;
107
- }
108
- handler({ type: "binary", value });
109
- }
110
- else {
111
- throw new TypeError("unexpected event data type");
112
- }
113
- });
114
- }
115
- subscribe(request) {
116
- const wsp = this.getWebSocketPool();
117
- if (request.type !== "subscribe") {
118
- throw new Error("Request must be a subscribe request");
119
- }
120
- wsp.addSubscription(request);
121
- }
122
- unsubscribe(subscriptionId) {
123
- const wsp = this.getWebSocketPool();
124
- wsp.removeSubscription(subscriptionId);
125
- }
126
- send(request) {
127
- const wsp = this.getWebSocketPool();
128
- wsp.sendRequest(request);
129
- }
130
- /**
131
- * Registers a handler function that will be called whenever all WebSocket connections are down or attempting to reconnect.
132
- * The connections may still try to reconnect in the background. To shut down the pool, call `shutdown()`.
133
- * @param handler - Function to be called when all connections are down
134
- */
135
- addAllConnectionsDownListener(handler) {
136
- const wsp = this.getWebSocketPool();
137
- wsp.addAllConnectionsDownListener(handler);
138
- }
139
- shutdown() {
140
- const wsp = this.getWebSocketPool();
141
- wsp.shutdown();
142
- }
143
- /**
144
- * Private helper method to make authenticated HTTP requests with Bearer token
145
- * @param url - The URL to fetch
146
- * @param options - Additional fetch options
147
- * @returns Promise resolving to the fetch Response
148
- */
149
- async authenticatedFetch(url, options = {}) {
150
- const headers = {
151
- Authorization: `Bearer ${this.token}`,
152
- ...options.headers,
153
- };
154
- return (0, cross_fetch_1.default)(url, {
155
- ...options,
156
- headers,
157
- });
158
- }
159
- /**
160
- * Queries the symbols endpoint to get available price feed symbols.
161
- * @param params - Optional query parameters to filter symbols
162
- * @returns Promise resolving to array of symbol information
163
- */
164
- async getSymbols(params) {
165
- const url = new URL(`${this.metadataServiceUrl}/v1/symbols`);
166
- if (params?.query) {
167
- url.searchParams.set("query", params.query);
168
- }
169
- if (params?.asset_type) {
170
- url.searchParams.set("asset_type", params.asset_type);
171
- }
172
- try {
173
- const response = await this.authenticatedFetch(url.toString());
174
- if (!response.ok) {
175
- throw new Error(`HTTP error! status: ${String(response.status)} - ${await response.text()}`);
176
- }
177
- return (await response.json());
178
- }
179
- catch (error) {
180
- throw new Error(`Failed to fetch symbols: ${error instanceof Error ? error.message : String(error)}`);
181
- }
182
- }
183
- /**
184
- * Queries the latest price endpoint to get current price data.
185
- * @param params - Parameters for the latest price request
186
- * @returns Promise resolving to JsonUpdate with current price data
187
- */
188
- async getLatestPrice(params) {
189
- const url = `${this.priceServiceUrl}/v1/latest_price`;
190
- try {
191
- const body = JSON.stringify(params);
192
- this.logger.debug("getLatestPrice", { url, body });
193
- const response = await this.authenticatedFetch(url, {
194
- method: "POST",
195
- headers: {
196
- "Content-Type": "application/json",
197
- },
198
- body: body,
199
- });
200
- if (!response.ok) {
201
- throw new Error(`HTTP error! status: ${String(response.status)} - ${await response.text()}`);
202
- }
203
- return (await response.json());
204
- }
205
- catch (error) {
206
- throw new Error(`Failed to fetch latest price: ${error instanceof Error ? error.message : String(error)}`);
207
- }
208
- }
209
- /**
210
- * Queries the price endpoint to get historical price data at a specific timestamp.
211
- * @param params - Parameters for the price request including timestamp
212
- * @returns Promise resolving to JsonUpdate with price data at the specified time
213
- */
214
- async getPrice(params) {
215
- const url = `${this.priceServiceUrl}/v1/price`;
216
- try {
217
- const body = JSON.stringify(params);
218
- this.logger.debug("getPrice", { url, body });
219
- const response = await this.authenticatedFetch(url, {
220
- method: "POST",
221
- headers: {
222
- "Content-Type": "application/json",
223
- },
224
- body: body,
225
- });
226
- if (!response.ok) {
227
- throw new Error(`HTTP error! status: ${String(response.status)} - ${await response.text()}`);
228
- }
229
- return (await response.json());
230
- }
231
- catch (error) {
232
- throw new Error(`Failed to fetch price: ${error instanceof Error ? error.message : String(error)}`);
233
- }
234
- }
235
- }
236
- exports.PythLazerClient = PythLazerClient;
@@ -1,9 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_STREAM_SERVICE_1_URL = exports.DEFAULT_STREAM_SERVICE_0_URL = exports.DEFAULT_PRICE_SERVICE_URL = exports.DEFAULT_METADATA_SERVICE_URL = exports.SOLANA_LAZER_STORAGE_ID = exports.SOLANA_LAZER_PROGRAM_ID = void 0;
4
- exports.SOLANA_LAZER_PROGRAM_ID = "pytd2yyk641x7ak7mkaasSJVXh6YYZnC7wTmtgAyxPt";
5
- exports.SOLANA_LAZER_STORAGE_ID = "3rdJbqfnagQ4yx9HXJViD4zc4xpiSqmFsKpPuSCQVyQL";
6
- exports.DEFAULT_METADATA_SERVICE_URL = "https://history.pyth-lazer.dourolabs.app/history";
7
- exports.DEFAULT_PRICE_SERVICE_URL = "https://pyth-lazer-0.dourolabs.app";
8
- exports.DEFAULT_STREAM_SERVICE_0_URL = "wss://pyth-lazer-0.dourolabs.app/v1/stream";
9
- exports.DEFAULT_STREAM_SERVICE_1_URL = "wss://pyth-lazer-1.dourolabs.app/v1/stream";
package/dist/cjs/index.js DELETED
@@ -1,19 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./client.js"), exports);
18
- __exportStar(require("./protocol.js"), exports);
19
- __exportStar(require("./constants.js"), exports);
@@ -1,11 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FORMAT_MAGICS_LE = exports.BINARY_UPDATE_FORMAT_MAGIC_LE = void 0;
4
- exports.BINARY_UPDATE_FORMAT_MAGIC_LE = 461_928_307;
5
- exports.FORMAT_MAGICS_LE = {
6
- JSON: 3_302_625_434,
7
- EVM: 2_593_727_018,
8
- SOLANA: 2_182_742_457,
9
- LE_ECDSA: 1_296_547_300,
10
- LE_UNSIGNED: 1_499_680_012,
11
- };
@@ -1,230 +0,0 @@
1
- import fetch from "cross-fetch";
2
- import WebSocket from "isomorphic-ws";
3
- import { dummyLogger } from "ts-log";
4
- import { DEFAULT_METADATA_SERVICE_URL, DEFAULT_PRICE_SERVICE_URL, } from "./constants.js";
5
- import { BINARY_UPDATE_FORMAT_MAGIC_LE, FORMAT_MAGICS_LE } from "./protocol.js";
6
- import { WebSocketPool } from "./socket/websocket-pool.js";
7
- const UINT16_NUM_BYTES = 2;
8
- const UINT32_NUM_BYTES = 4;
9
- const UINT64_NUM_BYTES = 8;
10
- export class PythLazerClient {
11
- token;
12
- metadataServiceUrl;
13
- priceServiceUrl;
14
- logger;
15
- wsp;
16
- constructor(token, metadataServiceUrl, priceServiceUrl, logger, wsp) {
17
- this.token = token;
18
- this.metadataServiceUrl = metadataServiceUrl;
19
- this.priceServiceUrl = priceServiceUrl;
20
- this.logger = logger;
21
- this.wsp = wsp;
22
- }
23
- /**
24
- * Gets the WebSocket pool. If the WebSocket pool is not configured, an error is thrown.
25
- * @throws Error if WebSocket pool is not configured
26
- * @returns The WebSocket pool
27
- */
28
- getWebSocketPool() {
29
- if (!this.wsp) {
30
- throw new Error("WebSocket pool is not available. Make sure to provide webSocketPoolConfig when creating the client.");
31
- }
32
- return this.wsp;
33
- }
34
- /**
35
- * Creates a new PythLazerClient instance.
36
- * @param config - Configuration including token, metadata service URL, and price service URL, and WebSocket pool configuration
37
- */
38
- static async create(config) {
39
- const token = config.token;
40
- // Collect and remove trailing slash from URLs
41
- const metadataServiceUrl = (config.metadataServiceUrl ?? DEFAULT_METADATA_SERVICE_URL).replace(/\/+$/, "");
42
- const priceServiceUrl = (config.priceServiceUrl ?? DEFAULT_PRICE_SERVICE_URL).replace(/\/+$/, "");
43
- const logger = config.logger ?? dummyLogger;
44
- // If webSocketPoolConfig is provided, create a WebSocket pool and block until at least one connection is established.
45
- let wsp;
46
- if (config.webSocketPoolConfig) {
47
- wsp = await WebSocketPool.create(config.webSocketPoolConfig, token, logger);
48
- }
49
- return new PythLazerClient(token, metadataServiceUrl, priceServiceUrl, logger, wsp);
50
- }
51
- /**
52
- * Adds a message listener that receives either JSON or binary responses from the WebSocket connections.
53
- * The listener will be called for each message received, with deduplication across redundant connections.
54
- * @param handler - Callback function that receives the parsed message. The message can be either a JSON response
55
- * or a binary response containing EVM, Solana, or parsed payload data.
56
- */
57
- addMessageListener(handler) {
58
- const wsp = this.getWebSocketPool();
59
- wsp.addMessageListener((data) => {
60
- if (typeof data == "string") {
61
- handler({
62
- type: "json",
63
- value: JSON.parse(data),
64
- });
65
- }
66
- else if (Buffer.isBuffer(data)) {
67
- let pos = 0;
68
- const magic = data.subarray(pos, pos + UINT32_NUM_BYTES).readUint32LE();
69
- pos += UINT32_NUM_BYTES;
70
- if (magic != BINARY_UPDATE_FORMAT_MAGIC_LE) {
71
- throw new Error("binary update format magic mismatch");
72
- }
73
- // TODO: some uint64 values may not be representable as Number.
74
- const subscriptionId = Number(data.subarray(pos, pos + UINT64_NUM_BYTES).readBigInt64BE());
75
- pos += UINT64_NUM_BYTES;
76
- const value = { subscriptionId };
77
- while (pos < data.length) {
78
- const len = data.subarray(pos, pos + UINT16_NUM_BYTES).readUint16BE();
79
- pos += UINT16_NUM_BYTES;
80
- const magic = data
81
- .subarray(pos, pos + UINT32_NUM_BYTES)
82
- .readUint32LE();
83
- if (magic == FORMAT_MAGICS_LE.EVM) {
84
- value.evm = data.subarray(pos, pos + len);
85
- }
86
- else if (magic == FORMAT_MAGICS_LE.SOLANA) {
87
- value.solana = data.subarray(pos, pos + len);
88
- }
89
- else if (magic == FORMAT_MAGICS_LE.LE_ECDSA) {
90
- value.leEcdsa = data.subarray(pos, pos + len);
91
- }
92
- else if (magic == FORMAT_MAGICS_LE.LE_UNSIGNED) {
93
- value.leUnsigned = data.subarray(pos, pos + len);
94
- }
95
- else if (magic == FORMAT_MAGICS_LE.JSON) {
96
- value.parsed = JSON.parse(data.subarray(pos + UINT32_NUM_BYTES, pos + len).toString());
97
- }
98
- else {
99
- throw new Error("unknown magic: " + magic.toString());
100
- }
101
- pos += len;
102
- }
103
- handler({ type: "binary", value });
104
- }
105
- else {
106
- throw new TypeError("unexpected event data type");
107
- }
108
- });
109
- }
110
- subscribe(request) {
111
- const wsp = this.getWebSocketPool();
112
- if (request.type !== "subscribe") {
113
- throw new Error("Request must be a subscribe request");
114
- }
115
- wsp.addSubscription(request);
116
- }
117
- unsubscribe(subscriptionId) {
118
- const wsp = this.getWebSocketPool();
119
- wsp.removeSubscription(subscriptionId);
120
- }
121
- send(request) {
122
- const wsp = this.getWebSocketPool();
123
- wsp.sendRequest(request);
124
- }
125
- /**
126
- * Registers a handler function that will be called whenever all WebSocket connections are down or attempting to reconnect.
127
- * The connections may still try to reconnect in the background. To shut down the pool, call `shutdown()`.
128
- * @param handler - Function to be called when all connections are down
129
- */
130
- addAllConnectionsDownListener(handler) {
131
- const wsp = this.getWebSocketPool();
132
- wsp.addAllConnectionsDownListener(handler);
133
- }
134
- shutdown() {
135
- const wsp = this.getWebSocketPool();
136
- wsp.shutdown();
137
- }
138
- /**
139
- * Private helper method to make authenticated HTTP requests with Bearer token
140
- * @param url - The URL to fetch
141
- * @param options - Additional fetch options
142
- * @returns Promise resolving to the fetch Response
143
- */
144
- async authenticatedFetch(url, options = {}) {
145
- const headers = {
146
- Authorization: `Bearer ${this.token}`,
147
- ...options.headers,
148
- };
149
- return fetch(url, {
150
- ...options,
151
- headers,
152
- });
153
- }
154
- /**
155
- * Queries the symbols endpoint to get available price feed symbols.
156
- * @param params - Optional query parameters to filter symbols
157
- * @returns Promise resolving to array of symbol information
158
- */
159
- async getSymbols(params) {
160
- const url = new URL(`${this.metadataServiceUrl}/v1/symbols`);
161
- if (params?.query) {
162
- url.searchParams.set("query", params.query);
163
- }
164
- if (params?.asset_type) {
165
- url.searchParams.set("asset_type", params.asset_type);
166
- }
167
- try {
168
- const response = await this.authenticatedFetch(url.toString());
169
- if (!response.ok) {
170
- throw new Error(`HTTP error! status: ${String(response.status)} - ${await response.text()}`);
171
- }
172
- return (await response.json());
173
- }
174
- catch (error) {
175
- throw new Error(`Failed to fetch symbols: ${error instanceof Error ? error.message : String(error)}`);
176
- }
177
- }
178
- /**
179
- * Queries the latest price endpoint to get current price data.
180
- * @param params - Parameters for the latest price request
181
- * @returns Promise resolving to JsonUpdate with current price data
182
- */
183
- async getLatestPrice(params) {
184
- const url = `${this.priceServiceUrl}/v1/latest_price`;
185
- try {
186
- const body = JSON.stringify(params);
187
- this.logger.debug("getLatestPrice", { url, body });
188
- const response = await this.authenticatedFetch(url, {
189
- method: "POST",
190
- headers: {
191
- "Content-Type": "application/json",
192
- },
193
- body: body,
194
- });
195
- if (!response.ok) {
196
- throw new Error(`HTTP error! status: ${String(response.status)} - ${await response.text()}`);
197
- }
198
- return (await response.json());
199
- }
200
- catch (error) {
201
- throw new Error(`Failed to fetch latest price: ${error instanceof Error ? error.message : String(error)}`);
202
- }
203
- }
204
- /**
205
- * Queries the price endpoint to get historical price data at a specific timestamp.
206
- * @param params - Parameters for the price request including timestamp
207
- * @returns Promise resolving to JsonUpdate with price data at the specified time
208
- */
209
- async getPrice(params) {
210
- const url = `${this.priceServiceUrl}/v1/price`;
211
- try {
212
- const body = JSON.stringify(params);
213
- this.logger.debug("getPrice", { url, body });
214
- const response = await this.authenticatedFetch(url, {
215
- method: "POST",
216
- headers: {
217
- "Content-Type": "application/json",
218
- },
219
- body: body,
220
- });
221
- if (!response.ok) {
222
- throw new Error(`HTTP error! status: ${String(response.status)} - ${await response.text()}`);
223
- }
224
- return (await response.json());
225
- }
226
- catch (error) {
227
- throw new Error(`Failed to fetch price: ${error instanceof Error ? error.message : String(error)}`);
228
- }
229
- }
230
- }
package/dist/esm/index.js DELETED
@@ -1,3 +0,0 @@
1
- export * from "./client.js";
2
- export * from "./protocol.js";
3
- export * from "./constants.js";