@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.
- package/README.md +57 -0
- package/dist/cjs/client.cjs +229 -0
- package/dist/cjs/constants.cjs +36 -0
- package/dist/cjs/index.cjs +20 -0
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/protocol.cjs +33 -0
- package/dist/cjs/protocol.d.ts +4 -1
- package/dist/cjs/socket/{resilient-websocket.js → resilient-websocket.cjs} +60 -45
- package/dist/cjs/socket/resilient-websocket.d.ts +15 -1
- package/dist/cjs/socket/{websocket-pool.js → websocket-pool.cjs} +77 -64
- package/dist/cjs/socket/websocket-pool.d.ts +5 -2
- package/dist/cjs/util/buffer-util.cjs +33 -0
- package/dist/cjs/util/buffer-util.d.ts +7 -0
- package/dist/cjs/util/env-util.cjs +33 -0
- package/dist/cjs/util/env-util.d.ts +17 -0
- package/dist/cjs/util/index.cjs +20 -0
- package/dist/cjs/util/index.d.ts +3 -0
- package/dist/cjs/util/url-util.cjs +17 -0
- package/dist/cjs/util/url-util.d.ts +8 -0
- package/dist/esm/client.mjs +219 -0
- package/dist/esm/index.mjs +3 -0
- package/dist/esm/package.json +1 -1
- package/dist/esm/protocol.d.ts +4 -1
- package/dist/esm/protocol.mjs +12 -0
- package/dist/esm/socket/resilient-websocket.d.ts +15 -1
- package/dist/esm/socket/{resilient-websocket.js → resilient-websocket.mjs} +42 -35
- package/dist/esm/socket/websocket-pool.d.ts +5 -2
- package/dist/esm/socket/{websocket-pool.js → websocket-pool.mjs} +58 -54
- package/dist/esm/util/buffer-util.d.ts +7 -0
- package/dist/esm/util/buffer-util.mjs +27 -0
- package/dist/esm/util/env-util.d.ts +17 -0
- package/dist/esm/util/env-util.mjs +23 -0
- package/dist/esm/util/index.d.ts +3 -0
- package/dist/esm/util/index.mjs +3 -0
- package/dist/esm/util/url-util.d.ts +8 -0
- package/dist/esm/util/url-util.mjs +13 -0
- package/package.json +111 -15
- package/dist/cjs/client.js +0 -236
- package/dist/cjs/constants.js +0 -9
- package/dist/cjs/index.js +0 -19
- package/dist/cjs/protocol.js +0 -11
- package/dist/esm/client.js +0 -230
- package/dist/esm/index.js +0 -3
- package/dist/esm/protocol.js +0 -8
- /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": "
|
|
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.
|
|
14
|
+
"main": "./dist/cjs/index.cjs",
|
|
12
15
|
"types": "./dist/cjs/index.d.ts",
|
|
13
16
|
"exports": {
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
|
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
|
}
|
package/dist/cjs/client.js
DELETED
|
@@ -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;
|
package/dist/cjs/constants.js
DELETED
|
@@ -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);
|
package/dist/cjs/protocol.js
DELETED
|
@@ -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
|
-
};
|
package/dist/esm/client.js
DELETED
|
@@ -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