@pythnetwork/pyth-lazer-sdk 1.0.0 → 3.0.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/dist/cjs/client.d.ts +47 -7
- package/dist/cjs/client.js +142 -13
- package/dist/cjs/constants.d.ts +6 -3
- package/dist/cjs/constants.js +7 -4
- package/dist/cjs/index.d.ts +0 -1
- package/dist/cjs/index.js +0 -1
- package/dist/cjs/protocol.d.ts +46 -0
- package/dist/cjs/socket/websocket-pool.d.ts +2 -4
- package/dist/cjs/socket/websocket-pool.js +11 -9
- package/dist/esm/client.d.ts +47 -7
- package/dist/esm/client.js +139 -13
- package/dist/esm/constants.d.ts +6 -3
- package/dist/esm/constants.js +6 -3
- package/dist/esm/index.d.ts +0 -1
- package/dist/esm/index.js +0 -1
- package/dist/esm/protocol.d.ts +46 -0
- package/dist/esm/socket/websocket-pool.d.ts +2 -4
- package/dist/esm/socket/websocket-pool.js +11 -9
- package/package.json +5 -4
- package/dist/cjs/ed25519.d.ts +0 -2
- package/dist/cjs/ed25519.js +0 -79
- package/dist/esm/ed25519.d.ts +0 -2
- package/dist/esm/ed25519.js +0 -42
package/dist/cjs/client.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Logger } from "ts-log";
|
|
2
|
+
import type { ParsedPayload, Request, Response, SymbolResponse, SymbolsQueryParams, LatestPriceRequest, PriceRequest, JsonUpdate } from "./protocol.js";
|
|
2
3
|
import type { WebSocketPoolConfig } from "./socket/websocket-pool.js";
|
|
3
4
|
export type BinaryResponse = {
|
|
4
5
|
subscriptionId: number;
|
|
@@ -15,17 +16,31 @@ export type JsonOrBinaryResponse = {
|
|
|
15
16
|
type: "binary";
|
|
16
17
|
value: BinaryResponse;
|
|
17
18
|
};
|
|
19
|
+
export type LazerClientConfig = {
|
|
20
|
+
token: string;
|
|
21
|
+
metadataServiceUrl?: string;
|
|
22
|
+
priceServiceUrl?: string;
|
|
23
|
+
logger?: Logger;
|
|
24
|
+
webSocketPoolConfig?: WebSocketPoolConfig;
|
|
25
|
+
};
|
|
18
26
|
export declare class PythLazerClient {
|
|
19
|
-
private readonly
|
|
27
|
+
private readonly token;
|
|
28
|
+
private readonly metadataServiceUrl;
|
|
29
|
+
private readonly priceServiceUrl;
|
|
30
|
+
private readonly logger;
|
|
31
|
+
private readonly wsp?;
|
|
20
32
|
private constructor();
|
|
33
|
+
/**
|
|
34
|
+
* Gets the WebSocket pool. If the WebSocket pool is not configured, an error is thrown.
|
|
35
|
+
* @throws Error if WebSocket pool is not configured
|
|
36
|
+
* @returns The WebSocket pool
|
|
37
|
+
*/
|
|
38
|
+
private getWebSocketPool;
|
|
21
39
|
/**
|
|
22
40
|
* Creates a new PythLazerClient instance.
|
|
23
|
-
* @param
|
|
24
|
-
* @param token - The access token for authentication
|
|
25
|
-
* @param numConnections - The number of parallel WebSocket connections to establish (default: 3). A higher number gives a more reliable stream. The connections will round-robin across the provided URLs.
|
|
26
|
-
* @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
|
|
41
|
+
* @param config - Configuration including token, metadata service URL, and price service URL, and WebSocket pool configuration
|
|
27
42
|
*/
|
|
28
|
-
static create(config:
|
|
43
|
+
static create(config: LazerClientConfig): Promise<PythLazerClient>;
|
|
29
44
|
/**
|
|
30
45
|
* Adds a message listener that receives either JSON or binary responses from the WebSocket connections.
|
|
31
46
|
* The listener will be called for each message received, with deduplication across redundant connections.
|
|
@@ -43,4 +58,29 @@ export declare class PythLazerClient {
|
|
|
43
58
|
*/
|
|
44
59
|
addAllConnectionsDownListener(handler: () => void): void;
|
|
45
60
|
shutdown(): void;
|
|
61
|
+
/**
|
|
62
|
+
* Private helper method to make authenticated HTTP requests with Bearer token
|
|
63
|
+
* @param url - The URL to fetch
|
|
64
|
+
* @param options - Additional fetch options
|
|
65
|
+
* @returns Promise resolving to the fetch Response
|
|
66
|
+
*/
|
|
67
|
+
private authenticatedFetch;
|
|
68
|
+
/**
|
|
69
|
+
* Queries the symbols endpoint to get available price feed symbols.
|
|
70
|
+
* @param params - Optional query parameters to filter symbols
|
|
71
|
+
* @returns Promise resolving to array of symbol information
|
|
72
|
+
*/
|
|
73
|
+
get_symbols(params?: SymbolsQueryParams): Promise<SymbolResponse[]>;
|
|
74
|
+
/**
|
|
75
|
+
* Queries the latest price endpoint to get current price data.
|
|
76
|
+
* @param params - Parameters for the latest price request
|
|
77
|
+
* @returns Promise resolving to JsonUpdate with current price data
|
|
78
|
+
*/
|
|
79
|
+
get_latest_price(params: LatestPriceRequest): Promise<JsonUpdate>;
|
|
80
|
+
/**
|
|
81
|
+
* Queries the price endpoint to get historical price data at a specific timestamp.
|
|
82
|
+
* @param params - Parameters for the price request including timestamp
|
|
83
|
+
* @returns Promise resolving to JsonUpdate with price data at the specified time
|
|
84
|
+
*/
|
|
85
|
+
get_price(params: PriceRequest): Promise<JsonUpdate>;
|
|
46
86
|
}
|
package/dist/cjs/client.js
CHANGED
|
@@ -1,26 +1,57 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
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");
|
|
4
10
|
const protocol_js_1 = require("./protocol.js");
|
|
5
11
|
const websocket_pool_js_1 = require("./socket/websocket-pool.js");
|
|
6
12
|
const UINT16_NUM_BYTES = 2;
|
|
7
13
|
const UINT32_NUM_BYTES = 4;
|
|
8
14
|
const UINT64_NUM_BYTES = 8;
|
|
9
15
|
class PythLazerClient {
|
|
16
|
+
token;
|
|
17
|
+
metadataServiceUrl;
|
|
18
|
+
priceServiceUrl;
|
|
19
|
+
logger;
|
|
10
20
|
wsp;
|
|
11
|
-
constructor(wsp) {
|
|
21
|
+
constructor(token, metadataServiceUrl, priceServiceUrl, logger, wsp) {
|
|
22
|
+
this.token = token;
|
|
23
|
+
this.metadataServiceUrl = metadataServiceUrl;
|
|
24
|
+
this.priceServiceUrl = priceServiceUrl;
|
|
25
|
+
this.logger = logger;
|
|
12
26
|
this.wsp = wsp;
|
|
13
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
|
+
}
|
|
14
39
|
/**
|
|
15
40
|
* Creates a new PythLazerClient instance.
|
|
16
|
-
* @param
|
|
17
|
-
* @param token - The access token for authentication
|
|
18
|
-
* @param numConnections - The number of parallel WebSocket connections to establish (default: 3). A higher number gives a more reliable stream. The connections will round-robin across the provided URLs.
|
|
19
|
-
* @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
|
|
41
|
+
* @param config - Configuration including token, metadata service URL, and price service URL, and WebSocket pool configuration
|
|
20
42
|
*/
|
|
21
43
|
static async create(config) {
|
|
22
|
-
const
|
|
23
|
-
|
|
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);
|
|
24
55
|
}
|
|
25
56
|
/**
|
|
26
57
|
* Adds a message listener that receives either JSON or binary responses from the WebSocket connections.
|
|
@@ -29,7 +60,8 @@ class PythLazerClient {
|
|
|
29
60
|
* or a binary response containing EVM, Solana, or parsed payload data.
|
|
30
61
|
*/
|
|
31
62
|
addMessageListener(handler) {
|
|
32
|
-
this.
|
|
63
|
+
const wsp = this.getWebSocketPool();
|
|
64
|
+
wsp.addMessageListener((data) => {
|
|
33
65
|
if (typeof data == "string") {
|
|
34
66
|
handler({
|
|
35
67
|
type: "json",
|
|
@@ -81,16 +113,19 @@ class PythLazerClient {
|
|
|
81
113
|
});
|
|
82
114
|
}
|
|
83
115
|
subscribe(request) {
|
|
116
|
+
const wsp = this.getWebSocketPool();
|
|
84
117
|
if (request.type !== "subscribe") {
|
|
85
118
|
throw new Error("Request must be a subscribe request");
|
|
86
119
|
}
|
|
87
|
-
|
|
120
|
+
wsp.addSubscription(request);
|
|
88
121
|
}
|
|
89
122
|
unsubscribe(subscriptionId) {
|
|
90
|
-
this.
|
|
123
|
+
const wsp = this.getWebSocketPool();
|
|
124
|
+
wsp.removeSubscription(subscriptionId);
|
|
91
125
|
}
|
|
92
126
|
send(request) {
|
|
93
|
-
this.
|
|
127
|
+
const wsp = this.getWebSocketPool();
|
|
128
|
+
wsp.sendRequest(request);
|
|
94
129
|
}
|
|
95
130
|
/**
|
|
96
131
|
* Registers a handler function that will be called whenever all WebSocket connections are down or attempting to reconnect.
|
|
@@ -98,10 +133,104 @@ class PythLazerClient {
|
|
|
98
133
|
* @param handler - Function to be called when all connections are down
|
|
99
134
|
*/
|
|
100
135
|
addAllConnectionsDownListener(handler) {
|
|
101
|
-
this.
|
|
136
|
+
const wsp = this.getWebSocketPool();
|
|
137
|
+
wsp.addAllConnectionsDownListener(handler);
|
|
102
138
|
}
|
|
103
139
|
shutdown() {
|
|
104
|
-
this.
|
|
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 get_symbols(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 get_latest_price(params) {
|
|
189
|
+
const url = `${this.priceServiceUrl}/v1/latest_price`;
|
|
190
|
+
try {
|
|
191
|
+
const body = JSON.stringify(params);
|
|
192
|
+
this.logger.debug("get_latest_price", { 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 get_price(params) {
|
|
215
|
+
const url = `${this.priceServiceUrl}/v1/price`;
|
|
216
|
+
try {
|
|
217
|
+
const body = JSON.stringify(params);
|
|
218
|
+
this.logger.debug("get_price", { 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
|
+
}
|
|
105
234
|
}
|
|
106
235
|
}
|
|
107
236
|
exports.PythLazerClient = PythLazerClient;
|
package/dist/cjs/constants.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const
|
|
1
|
+
export declare const SOLANA_LAZER_PROGRAM_ID = "pytd2yyk641x7ak7mkaasSJVXh6YYZnC7wTmtgAyxPt";
|
|
2
|
+
export declare const SOLANA_LAZER_STORAGE_ID = "3rdJbqfnagQ4yx9HXJViD4zc4xpiSqmFsKpPuSCQVyQL";
|
|
3
|
+
export declare const DEFAULT_METADATA_SERVICE_URL = "https://history.pyth-lazer.dourolabs.app/history";
|
|
4
|
+
export declare const DEFAULT_PRICE_SERVICE_URL = "https://pyth-lazer-0.dourolabs.app";
|
|
5
|
+
export declare const DEFAULT_STREAM_SERVICE_0_URL = "wss://pyth-lazer-0.dourolabs.app/v1/stream";
|
|
6
|
+
export declare const DEFAULT_STREAM_SERVICE_1_URL = "wss://pyth-lazer-1.dourolabs.app/v1/stream";
|
package/dist/cjs/constants.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
5
|
-
exports.
|
|
6
|
-
exports.
|
|
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.d.ts
CHANGED
package/dist/cjs/index.js
CHANGED
|
@@ -16,5 +16,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./client.js"), exports);
|
|
18
18
|
__exportStar(require("./protocol.js"), exports);
|
|
19
|
-
__exportStar(require("./ed25519.js"), exports);
|
|
20
19
|
__exportStar(require("./constants.js"), exports);
|
package/dist/cjs/protocol.d.ts
CHANGED
|
@@ -75,3 +75,49 @@ export declare const FORMAT_MAGICS_LE: {
|
|
|
75
75
|
LE_ECDSA: number;
|
|
76
76
|
LE_UNSIGNED: number;
|
|
77
77
|
};
|
|
78
|
+
export type AssetType = "crypto" | "fx" | "equity" | "metal" | "rates" | "nav" | "commodity" | "funding-rate";
|
|
79
|
+
export type SymbolsQueryParams = {
|
|
80
|
+
query?: string;
|
|
81
|
+
asset_type?: AssetType;
|
|
82
|
+
};
|
|
83
|
+
export type SymbolResponse = {
|
|
84
|
+
pyth_lazer_id: number;
|
|
85
|
+
name: string;
|
|
86
|
+
symbol: string;
|
|
87
|
+
description: string;
|
|
88
|
+
asset_type: string;
|
|
89
|
+
exponent: number;
|
|
90
|
+
min_publishers: number;
|
|
91
|
+
min_channel: string;
|
|
92
|
+
state: string;
|
|
93
|
+
schedule: string;
|
|
94
|
+
cmc_id?: number | null;
|
|
95
|
+
hermes_id?: string | null;
|
|
96
|
+
interval?: string | null;
|
|
97
|
+
};
|
|
98
|
+
export type LatestPriceRequest = {
|
|
99
|
+
priceFeedIds?: number[];
|
|
100
|
+
symbols?: string[];
|
|
101
|
+
properties: PriceFeedProperty[];
|
|
102
|
+
formats: Format[];
|
|
103
|
+
jsonBinaryEncoding?: JsonBinaryEncoding;
|
|
104
|
+
parsed?: boolean;
|
|
105
|
+
channel: Channel;
|
|
106
|
+
};
|
|
107
|
+
export type PriceRequest = {
|
|
108
|
+
timestamp: number;
|
|
109
|
+
priceFeedIds?: number[];
|
|
110
|
+
symbols?: string[];
|
|
111
|
+
properties: PriceFeedProperty[];
|
|
112
|
+
formats: Format[];
|
|
113
|
+
jsonBinaryEncoding?: JsonBinaryEncoding;
|
|
114
|
+
parsed?: boolean;
|
|
115
|
+
channel: Channel;
|
|
116
|
+
};
|
|
117
|
+
export type JsonUpdate = {
|
|
118
|
+
parsed?: ParsedPayload;
|
|
119
|
+
evm?: JsonBinaryData;
|
|
120
|
+
solana?: JsonBinaryData;
|
|
121
|
+
leEcdsa?: JsonBinaryData;
|
|
122
|
+
leUnsigned?: JsonBinaryData;
|
|
123
|
+
};
|
|
@@ -5,10 +5,8 @@ import type { Request } from "../protocol.js";
|
|
|
5
5
|
import type { ResilientWebSocketConfig } from "./resilient-websocket.js";
|
|
6
6
|
import { ResilientWebSocket } from "./resilient-websocket.js";
|
|
7
7
|
export type WebSocketPoolConfig = {
|
|
8
|
-
urls
|
|
9
|
-
token: string;
|
|
8
|
+
urls?: string[];
|
|
10
9
|
numConnections?: number;
|
|
11
|
-
logger?: Logger;
|
|
12
10
|
rwsConfig?: Omit<ResilientWebSocketConfig, "logger" | "endpoint">;
|
|
13
11
|
onError?: (error: ErrorEvent) => void;
|
|
14
12
|
};
|
|
@@ -30,7 +28,7 @@ export declare class WebSocketPool {
|
|
|
30
28
|
* @param numConnections - Number of parallel WebSocket connections to maintain (default: 3)
|
|
31
29
|
* @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
|
|
32
30
|
*/
|
|
33
|
-
static create(config: WebSocketPoolConfig): Promise<WebSocketPool>;
|
|
31
|
+
static create(config: WebSocketPoolConfig, token: string, logger?: Logger): Promise<WebSocketPool>;
|
|
34
32
|
/**
|
|
35
33
|
* Checks for error responses in JSON messages and throws appropriate errors
|
|
36
34
|
*/
|
|
@@ -7,6 +7,7 @@ exports.WebSocketPool = void 0;
|
|
|
7
7
|
const ttlcache_1 = __importDefault(require("@isaacs/ttlcache"));
|
|
8
8
|
const ts_log_1 = require("ts-log");
|
|
9
9
|
const resilient_websocket_js_1 = require("./resilient-websocket.js");
|
|
10
|
+
const constants_js_1 = require("../constants.js");
|
|
10
11
|
const DEFAULT_NUM_CONNECTIONS = 4;
|
|
11
12
|
class WebSocketPool {
|
|
12
13
|
logger;
|
|
@@ -37,29 +38,30 @@ class WebSocketPool {
|
|
|
37
38
|
* @param numConnections - Number of parallel WebSocket connections to maintain (default: 3)
|
|
38
39
|
* @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
|
|
39
40
|
*/
|
|
40
|
-
static async create(config) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
41
|
+
static async create(config, token, logger) {
|
|
42
|
+
const urls = config.urls ?? [
|
|
43
|
+
constants_js_1.DEFAULT_STREAM_SERVICE_0_URL,
|
|
44
|
+
constants_js_1.DEFAULT_STREAM_SERVICE_1_URL,
|
|
45
|
+
];
|
|
46
|
+
const log = logger ?? ts_log_1.dummyLogger;
|
|
47
|
+
const pool = new WebSocketPool(log);
|
|
46
48
|
const numConnections = config.numConnections ?? DEFAULT_NUM_CONNECTIONS;
|
|
47
49
|
for (let i = 0; i < numConnections; i++) {
|
|
48
|
-
const url =
|
|
50
|
+
const url = urls[i % urls.length];
|
|
49
51
|
if (!url) {
|
|
50
52
|
throw new Error(`URLs must not be null or empty`);
|
|
51
53
|
}
|
|
52
54
|
const wsOptions = {
|
|
53
55
|
...config.rwsConfig?.wsOptions,
|
|
54
56
|
headers: {
|
|
55
|
-
Authorization: `Bearer ${
|
|
57
|
+
Authorization: `Bearer ${token}`,
|
|
56
58
|
},
|
|
57
59
|
};
|
|
58
60
|
const rws = new resilient_websocket_js_1.ResilientWebSocket({
|
|
59
61
|
...config.rwsConfig,
|
|
60
62
|
endpoint: url,
|
|
61
63
|
wsOptions,
|
|
62
|
-
logger,
|
|
64
|
+
logger: log,
|
|
63
65
|
});
|
|
64
66
|
// If a websocket client unexpectedly disconnects, ResilientWebSocket will reestablish
|
|
65
67
|
// the connection and call the onReconnect callback.
|
package/dist/esm/client.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Logger } from "ts-log";
|
|
2
|
+
import type { ParsedPayload, Request, Response, SymbolResponse, SymbolsQueryParams, LatestPriceRequest, PriceRequest, JsonUpdate } from "./protocol.js";
|
|
2
3
|
import type { WebSocketPoolConfig } from "./socket/websocket-pool.js";
|
|
3
4
|
export type BinaryResponse = {
|
|
4
5
|
subscriptionId: number;
|
|
@@ -15,17 +16,31 @@ export type JsonOrBinaryResponse = {
|
|
|
15
16
|
type: "binary";
|
|
16
17
|
value: BinaryResponse;
|
|
17
18
|
};
|
|
19
|
+
export type LazerClientConfig = {
|
|
20
|
+
token: string;
|
|
21
|
+
metadataServiceUrl?: string;
|
|
22
|
+
priceServiceUrl?: string;
|
|
23
|
+
logger?: Logger;
|
|
24
|
+
webSocketPoolConfig?: WebSocketPoolConfig;
|
|
25
|
+
};
|
|
18
26
|
export declare class PythLazerClient {
|
|
19
|
-
private readonly
|
|
27
|
+
private readonly token;
|
|
28
|
+
private readonly metadataServiceUrl;
|
|
29
|
+
private readonly priceServiceUrl;
|
|
30
|
+
private readonly logger;
|
|
31
|
+
private readonly wsp?;
|
|
20
32
|
private constructor();
|
|
33
|
+
/**
|
|
34
|
+
* Gets the WebSocket pool. If the WebSocket pool is not configured, an error is thrown.
|
|
35
|
+
* @throws Error if WebSocket pool is not configured
|
|
36
|
+
* @returns The WebSocket pool
|
|
37
|
+
*/
|
|
38
|
+
private getWebSocketPool;
|
|
21
39
|
/**
|
|
22
40
|
* Creates a new PythLazerClient instance.
|
|
23
|
-
* @param
|
|
24
|
-
* @param token - The access token for authentication
|
|
25
|
-
* @param numConnections - The number of parallel WebSocket connections to establish (default: 3). A higher number gives a more reliable stream. The connections will round-robin across the provided URLs.
|
|
26
|
-
* @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
|
|
41
|
+
* @param config - Configuration including token, metadata service URL, and price service URL, and WebSocket pool configuration
|
|
27
42
|
*/
|
|
28
|
-
static create(config:
|
|
43
|
+
static create(config: LazerClientConfig): Promise<PythLazerClient>;
|
|
29
44
|
/**
|
|
30
45
|
* Adds a message listener that receives either JSON or binary responses from the WebSocket connections.
|
|
31
46
|
* The listener will be called for each message received, with deduplication across redundant connections.
|
|
@@ -43,4 +58,29 @@ export declare class PythLazerClient {
|
|
|
43
58
|
*/
|
|
44
59
|
addAllConnectionsDownListener(handler: () => void): void;
|
|
45
60
|
shutdown(): void;
|
|
61
|
+
/**
|
|
62
|
+
* Private helper method to make authenticated HTTP requests with Bearer token
|
|
63
|
+
* @param url - The URL to fetch
|
|
64
|
+
* @param options - Additional fetch options
|
|
65
|
+
* @returns Promise resolving to the fetch Response
|
|
66
|
+
*/
|
|
67
|
+
private authenticatedFetch;
|
|
68
|
+
/**
|
|
69
|
+
* Queries the symbols endpoint to get available price feed symbols.
|
|
70
|
+
* @param params - Optional query parameters to filter symbols
|
|
71
|
+
* @returns Promise resolving to array of symbol information
|
|
72
|
+
*/
|
|
73
|
+
get_symbols(params?: SymbolsQueryParams): Promise<SymbolResponse[]>;
|
|
74
|
+
/**
|
|
75
|
+
* Queries the latest price endpoint to get current price data.
|
|
76
|
+
* @param params - Parameters for the latest price request
|
|
77
|
+
* @returns Promise resolving to JsonUpdate with current price data
|
|
78
|
+
*/
|
|
79
|
+
get_latest_price(params: LatestPriceRequest): Promise<JsonUpdate>;
|
|
80
|
+
/**
|
|
81
|
+
* Queries the price endpoint to get historical price data at a specific timestamp.
|
|
82
|
+
* @param params - Parameters for the price request including timestamp
|
|
83
|
+
* @returns Promise resolving to JsonUpdate with price data at the specified time
|
|
84
|
+
*/
|
|
85
|
+
get_price(params: PriceRequest): Promise<JsonUpdate>;
|
|
46
86
|
}
|
package/dist/esm/client.js
CHANGED
|
@@ -1,24 +1,52 @@
|
|
|
1
|
+
import fetch from "cross-fetch";
|
|
1
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";
|
|
2
5
|
import { BINARY_UPDATE_FORMAT_MAGIC_LE, FORMAT_MAGICS_LE } from "./protocol.js";
|
|
3
6
|
import { WebSocketPool } from "./socket/websocket-pool.js";
|
|
4
7
|
const UINT16_NUM_BYTES = 2;
|
|
5
8
|
const UINT32_NUM_BYTES = 4;
|
|
6
9
|
const UINT64_NUM_BYTES = 8;
|
|
7
10
|
export class PythLazerClient {
|
|
11
|
+
token;
|
|
12
|
+
metadataServiceUrl;
|
|
13
|
+
priceServiceUrl;
|
|
14
|
+
logger;
|
|
8
15
|
wsp;
|
|
9
|
-
constructor(wsp) {
|
|
16
|
+
constructor(token, metadataServiceUrl, priceServiceUrl, logger, wsp) {
|
|
17
|
+
this.token = token;
|
|
18
|
+
this.metadataServiceUrl = metadataServiceUrl;
|
|
19
|
+
this.priceServiceUrl = priceServiceUrl;
|
|
20
|
+
this.logger = logger;
|
|
10
21
|
this.wsp = wsp;
|
|
11
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
|
+
}
|
|
12
34
|
/**
|
|
13
35
|
* Creates a new PythLazerClient instance.
|
|
14
|
-
* @param
|
|
15
|
-
* @param token - The access token for authentication
|
|
16
|
-
* @param numConnections - The number of parallel WebSocket connections to establish (default: 3). A higher number gives a more reliable stream. The connections will round-robin across the provided URLs.
|
|
17
|
-
* @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
|
|
36
|
+
* @param config - Configuration including token, metadata service URL, and price service URL, and WebSocket pool configuration
|
|
18
37
|
*/
|
|
19
38
|
static async create(config) {
|
|
20
|
-
const
|
|
21
|
-
|
|
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);
|
|
22
50
|
}
|
|
23
51
|
/**
|
|
24
52
|
* Adds a message listener that receives either JSON or binary responses from the WebSocket connections.
|
|
@@ -27,7 +55,8 @@ export class PythLazerClient {
|
|
|
27
55
|
* or a binary response containing EVM, Solana, or parsed payload data.
|
|
28
56
|
*/
|
|
29
57
|
addMessageListener(handler) {
|
|
30
|
-
this.
|
|
58
|
+
const wsp = this.getWebSocketPool();
|
|
59
|
+
wsp.addMessageListener((data) => {
|
|
31
60
|
if (typeof data == "string") {
|
|
32
61
|
handler({
|
|
33
62
|
type: "json",
|
|
@@ -79,16 +108,19 @@ export class PythLazerClient {
|
|
|
79
108
|
});
|
|
80
109
|
}
|
|
81
110
|
subscribe(request) {
|
|
111
|
+
const wsp = this.getWebSocketPool();
|
|
82
112
|
if (request.type !== "subscribe") {
|
|
83
113
|
throw new Error("Request must be a subscribe request");
|
|
84
114
|
}
|
|
85
|
-
|
|
115
|
+
wsp.addSubscription(request);
|
|
86
116
|
}
|
|
87
117
|
unsubscribe(subscriptionId) {
|
|
88
|
-
this.
|
|
118
|
+
const wsp = this.getWebSocketPool();
|
|
119
|
+
wsp.removeSubscription(subscriptionId);
|
|
89
120
|
}
|
|
90
121
|
send(request) {
|
|
91
|
-
this.
|
|
122
|
+
const wsp = this.getWebSocketPool();
|
|
123
|
+
wsp.sendRequest(request);
|
|
92
124
|
}
|
|
93
125
|
/**
|
|
94
126
|
* Registers a handler function that will be called whenever all WebSocket connections are down or attempting to reconnect.
|
|
@@ -96,9 +128,103 @@ export class PythLazerClient {
|
|
|
96
128
|
* @param handler - Function to be called when all connections are down
|
|
97
129
|
*/
|
|
98
130
|
addAllConnectionsDownListener(handler) {
|
|
99
|
-
this.
|
|
131
|
+
const wsp = this.getWebSocketPool();
|
|
132
|
+
wsp.addAllConnectionsDownListener(handler);
|
|
100
133
|
}
|
|
101
134
|
shutdown() {
|
|
102
|
-
this.
|
|
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 get_symbols(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 get_latest_price(params) {
|
|
184
|
+
const url = `${this.priceServiceUrl}/v1/latest_price`;
|
|
185
|
+
try {
|
|
186
|
+
const body = JSON.stringify(params);
|
|
187
|
+
this.logger.debug("get_latest_price", { 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 get_price(params) {
|
|
210
|
+
const url = `${this.priceServiceUrl}/v1/price`;
|
|
211
|
+
try {
|
|
212
|
+
const body = JSON.stringify(params);
|
|
213
|
+
this.logger.debug("get_price", { 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
|
+
}
|
|
103
229
|
}
|
|
104
230
|
}
|
package/dist/esm/constants.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const
|
|
1
|
+
export declare const SOLANA_LAZER_PROGRAM_ID = "pytd2yyk641x7ak7mkaasSJVXh6YYZnC7wTmtgAyxPt";
|
|
2
|
+
export declare const SOLANA_LAZER_STORAGE_ID = "3rdJbqfnagQ4yx9HXJViD4zc4xpiSqmFsKpPuSCQVyQL";
|
|
3
|
+
export declare const DEFAULT_METADATA_SERVICE_URL = "https://history.pyth-lazer.dourolabs.app/history";
|
|
4
|
+
export declare const DEFAULT_PRICE_SERVICE_URL = "https://pyth-lazer-0.dourolabs.app";
|
|
5
|
+
export declare const DEFAULT_STREAM_SERVICE_0_URL = "wss://pyth-lazer-0.dourolabs.app/v1/stream";
|
|
6
|
+
export declare const DEFAULT_STREAM_SERVICE_1_URL = "wss://pyth-lazer-1.dourolabs.app/v1/stream";
|
package/dist/esm/constants.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
export const
|
|
3
|
-
export const
|
|
1
|
+
export const SOLANA_LAZER_PROGRAM_ID = "pytd2yyk641x7ak7mkaasSJVXh6YYZnC7wTmtgAyxPt";
|
|
2
|
+
export const SOLANA_LAZER_STORAGE_ID = "3rdJbqfnagQ4yx9HXJViD4zc4xpiSqmFsKpPuSCQVyQL";
|
|
3
|
+
export const DEFAULT_METADATA_SERVICE_URL = "https://history.pyth-lazer.dourolabs.app/history";
|
|
4
|
+
export const DEFAULT_PRICE_SERVICE_URL = "https://pyth-lazer-0.dourolabs.app";
|
|
5
|
+
export const DEFAULT_STREAM_SERVICE_0_URL = "wss://pyth-lazer-0.dourolabs.app/v1/stream";
|
|
6
|
+
export const DEFAULT_STREAM_SERVICE_1_URL = "wss://pyth-lazer-1.dourolabs.app/v1/stream";
|
package/dist/esm/index.d.ts
CHANGED
package/dist/esm/index.js
CHANGED
package/dist/esm/protocol.d.ts
CHANGED
|
@@ -75,3 +75,49 @@ export declare const FORMAT_MAGICS_LE: {
|
|
|
75
75
|
LE_ECDSA: number;
|
|
76
76
|
LE_UNSIGNED: number;
|
|
77
77
|
};
|
|
78
|
+
export type AssetType = "crypto" | "fx" | "equity" | "metal" | "rates" | "nav" | "commodity" | "funding-rate";
|
|
79
|
+
export type SymbolsQueryParams = {
|
|
80
|
+
query?: string;
|
|
81
|
+
asset_type?: AssetType;
|
|
82
|
+
};
|
|
83
|
+
export type SymbolResponse = {
|
|
84
|
+
pyth_lazer_id: number;
|
|
85
|
+
name: string;
|
|
86
|
+
symbol: string;
|
|
87
|
+
description: string;
|
|
88
|
+
asset_type: string;
|
|
89
|
+
exponent: number;
|
|
90
|
+
min_publishers: number;
|
|
91
|
+
min_channel: string;
|
|
92
|
+
state: string;
|
|
93
|
+
schedule: string;
|
|
94
|
+
cmc_id?: number | null;
|
|
95
|
+
hermes_id?: string | null;
|
|
96
|
+
interval?: string | null;
|
|
97
|
+
};
|
|
98
|
+
export type LatestPriceRequest = {
|
|
99
|
+
priceFeedIds?: number[];
|
|
100
|
+
symbols?: string[];
|
|
101
|
+
properties: PriceFeedProperty[];
|
|
102
|
+
formats: Format[];
|
|
103
|
+
jsonBinaryEncoding?: JsonBinaryEncoding;
|
|
104
|
+
parsed?: boolean;
|
|
105
|
+
channel: Channel;
|
|
106
|
+
};
|
|
107
|
+
export type PriceRequest = {
|
|
108
|
+
timestamp: number;
|
|
109
|
+
priceFeedIds?: number[];
|
|
110
|
+
symbols?: string[];
|
|
111
|
+
properties: PriceFeedProperty[];
|
|
112
|
+
formats: Format[];
|
|
113
|
+
jsonBinaryEncoding?: JsonBinaryEncoding;
|
|
114
|
+
parsed?: boolean;
|
|
115
|
+
channel: Channel;
|
|
116
|
+
};
|
|
117
|
+
export type JsonUpdate = {
|
|
118
|
+
parsed?: ParsedPayload;
|
|
119
|
+
evm?: JsonBinaryData;
|
|
120
|
+
solana?: JsonBinaryData;
|
|
121
|
+
leEcdsa?: JsonBinaryData;
|
|
122
|
+
leUnsigned?: JsonBinaryData;
|
|
123
|
+
};
|
|
@@ -5,10 +5,8 @@ import type { Request } from "../protocol.js";
|
|
|
5
5
|
import type { ResilientWebSocketConfig } from "./resilient-websocket.js";
|
|
6
6
|
import { ResilientWebSocket } from "./resilient-websocket.js";
|
|
7
7
|
export type WebSocketPoolConfig = {
|
|
8
|
-
urls
|
|
9
|
-
token: string;
|
|
8
|
+
urls?: string[];
|
|
10
9
|
numConnections?: number;
|
|
11
|
-
logger?: Logger;
|
|
12
10
|
rwsConfig?: Omit<ResilientWebSocketConfig, "logger" | "endpoint">;
|
|
13
11
|
onError?: (error: ErrorEvent) => void;
|
|
14
12
|
};
|
|
@@ -30,7 +28,7 @@ export declare class WebSocketPool {
|
|
|
30
28
|
* @param numConnections - Number of parallel WebSocket connections to maintain (default: 3)
|
|
31
29
|
* @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
|
|
32
30
|
*/
|
|
33
|
-
static create(config: WebSocketPoolConfig): Promise<WebSocketPool>;
|
|
31
|
+
static create(config: WebSocketPoolConfig, token: string, logger?: Logger): Promise<WebSocketPool>;
|
|
34
32
|
/**
|
|
35
33
|
* Checks for error responses in JSON messages and throws appropriate errors
|
|
36
34
|
*/
|
|
@@ -2,6 +2,7 @@ import TTLCache from "@isaacs/ttlcache";
|
|
|
2
2
|
import WebSocket from "isomorphic-ws";
|
|
3
3
|
import { dummyLogger } from "ts-log";
|
|
4
4
|
import { ResilientWebSocket } from "./resilient-websocket.js";
|
|
5
|
+
import { DEFAULT_STREAM_SERVICE_0_URL, DEFAULT_STREAM_SERVICE_1_URL, } from "../constants.js";
|
|
5
6
|
const DEFAULT_NUM_CONNECTIONS = 4;
|
|
6
7
|
export class WebSocketPool {
|
|
7
8
|
logger;
|
|
@@ -32,29 +33,30 @@ export class WebSocketPool {
|
|
|
32
33
|
* @param numConnections - Number of parallel WebSocket connections to maintain (default: 3)
|
|
33
34
|
* @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
|
|
34
35
|
*/
|
|
35
|
-
static async create(config) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
36
|
+
static async create(config, token, logger) {
|
|
37
|
+
const urls = config.urls ?? [
|
|
38
|
+
DEFAULT_STREAM_SERVICE_0_URL,
|
|
39
|
+
DEFAULT_STREAM_SERVICE_1_URL,
|
|
40
|
+
];
|
|
41
|
+
const log = logger ?? dummyLogger;
|
|
42
|
+
const pool = new WebSocketPool(log);
|
|
41
43
|
const numConnections = config.numConnections ?? DEFAULT_NUM_CONNECTIONS;
|
|
42
44
|
for (let i = 0; i < numConnections; i++) {
|
|
43
|
-
const url =
|
|
45
|
+
const url = urls[i % urls.length];
|
|
44
46
|
if (!url) {
|
|
45
47
|
throw new Error(`URLs must not be null or empty`);
|
|
46
48
|
}
|
|
47
49
|
const wsOptions = {
|
|
48
50
|
...config.rwsConfig?.wsOptions,
|
|
49
51
|
headers: {
|
|
50
|
-
Authorization: `Bearer ${
|
|
52
|
+
Authorization: `Bearer ${token}`,
|
|
51
53
|
},
|
|
52
54
|
};
|
|
53
55
|
const rws = new ResilientWebSocket({
|
|
54
56
|
...config.rwsConfig,
|
|
55
57
|
endpoint: url,
|
|
56
58
|
wsOptions,
|
|
57
|
-
logger,
|
|
59
|
+
logger: log,
|
|
58
60
|
});
|
|
59
61
|
// If a websocket client unexpectedly disconnects, ResilientWebSocket will reestablish
|
|
60
62
|
// the connection and call the onReconnect callback.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pythnetwork/pyth-lazer-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Pyth Lazer SDK",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -48,8 +48,7 @@
|
|
|
48
48
|
"license": "Apache-2.0",
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@isaacs/ttlcache": "^1.4.1",
|
|
51
|
-
"
|
|
52
|
-
"@solana/web3.js": "^1.98.0",
|
|
51
|
+
"cross-fetch": "^4.0.0",
|
|
53
52
|
"isomorphic-ws": "^5.0.0",
|
|
54
53
|
"ts-log": "^2.2.7",
|
|
55
54
|
"ws": "^8.18.0"
|
|
@@ -62,7 +61,9 @@
|
|
|
62
61
|
"test:types": "tsc",
|
|
63
62
|
"test:format": "prettier --check .",
|
|
64
63
|
"fix:format": "prettier --write .",
|
|
65
|
-
"example": "node --loader ts-node/esm examples/
|
|
64
|
+
"example:streaming": "node --loader ts-node/esm examples/streaming.js",
|
|
65
|
+
"example:history": "node --loader ts-node/esm examples/history.js",
|
|
66
|
+
"example:symbols": "node --loader ts-node/esm examples/symbols.js",
|
|
66
67
|
"doc": "typedoc --out docs/typedoc src"
|
|
67
68
|
}
|
|
68
69
|
}
|
package/dist/cjs/ed25519.d.ts
DELETED
package/dist/cjs/ed25519.js
DELETED
|
@@ -1,79 +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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.createEd25519Instruction = void 0;
|
|
37
|
-
const BufferLayout = __importStar(require("@solana/buffer-layout"));
|
|
38
|
-
const web3_js_1 = require("@solana/web3.js");
|
|
39
|
-
const ED25519_INSTRUCTION_LEN = 16;
|
|
40
|
-
const SIGNATURE_LEN = 64;
|
|
41
|
-
const PUBKEY_LEN = 32;
|
|
42
|
-
const MAGIC_LEN = 4;
|
|
43
|
-
const MESSAGE_SIZE_LEN = 2;
|
|
44
|
-
const ED25519_INSTRUCTION_LAYOUT = BufferLayout.struct([
|
|
45
|
-
BufferLayout.u8("numSignatures"),
|
|
46
|
-
BufferLayout.u8("padding"),
|
|
47
|
-
BufferLayout.u16("signatureOffset"),
|
|
48
|
-
BufferLayout.u16("signatureInstructionIndex"),
|
|
49
|
-
BufferLayout.u16("publicKeyOffset"),
|
|
50
|
-
BufferLayout.u16("publicKeyInstructionIndex"),
|
|
51
|
-
BufferLayout.u16("messageDataOffset"),
|
|
52
|
-
BufferLayout.u16("messageDataSize"),
|
|
53
|
-
BufferLayout.u16("messageInstructionIndex"),
|
|
54
|
-
]);
|
|
55
|
-
const createEd25519Instruction = (message, instructionIndex, startingOffset) => {
|
|
56
|
-
const signatureOffset = startingOffset + MAGIC_LEN;
|
|
57
|
-
const publicKeyOffset = signatureOffset + SIGNATURE_LEN;
|
|
58
|
-
const messageDataSizeOffset = publicKeyOffset + PUBKEY_LEN;
|
|
59
|
-
const messageDataOffset = messageDataSizeOffset + MESSAGE_SIZE_LEN;
|
|
60
|
-
const messageDataSize = message.readUInt16LE(messageDataSizeOffset - startingOffset);
|
|
61
|
-
const instructionData = Buffer.alloc(ED25519_INSTRUCTION_LEN);
|
|
62
|
-
ED25519_INSTRUCTION_LAYOUT.encode({
|
|
63
|
-
numSignatures: 1,
|
|
64
|
-
padding: 0,
|
|
65
|
-
signatureOffset,
|
|
66
|
-
signatureInstructionIndex: instructionIndex,
|
|
67
|
-
publicKeyOffset,
|
|
68
|
-
publicKeyInstructionIndex: instructionIndex,
|
|
69
|
-
messageDataOffset,
|
|
70
|
-
messageDataSize: messageDataSize,
|
|
71
|
-
messageInstructionIndex: instructionIndex,
|
|
72
|
-
}, instructionData);
|
|
73
|
-
return new web3_js_1.TransactionInstruction({
|
|
74
|
-
keys: [],
|
|
75
|
-
programId: web3_js_1.Ed25519Program.programId,
|
|
76
|
-
data: instructionData,
|
|
77
|
-
});
|
|
78
|
-
};
|
|
79
|
-
exports.createEd25519Instruction = createEd25519Instruction;
|
package/dist/esm/ed25519.d.ts
DELETED
package/dist/esm/ed25519.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import * as BufferLayout from "@solana/buffer-layout";
|
|
2
|
-
import { Ed25519Program, TransactionInstruction } from "@solana/web3.js";
|
|
3
|
-
const ED25519_INSTRUCTION_LEN = 16;
|
|
4
|
-
const SIGNATURE_LEN = 64;
|
|
5
|
-
const PUBKEY_LEN = 32;
|
|
6
|
-
const MAGIC_LEN = 4;
|
|
7
|
-
const MESSAGE_SIZE_LEN = 2;
|
|
8
|
-
const ED25519_INSTRUCTION_LAYOUT = BufferLayout.struct([
|
|
9
|
-
BufferLayout.u8("numSignatures"),
|
|
10
|
-
BufferLayout.u8("padding"),
|
|
11
|
-
BufferLayout.u16("signatureOffset"),
|
|
12
|
-
BufferLayout.u16("signatureInstructionIndex"),
|
|
13
|
-
BufferLayout.u16("publicKeyOffset"),
|
|
14
|
-
BufferLayout.u16("publicKeyInstructionIndex"),
|
|
15
|
-
BufferLayout.u16("messageDataOffset"),
|
|
16
|
-
BufferLayout.u16("messageDataSize"),
|
|
17
|
-
BufferLayout.u16("messageInstructionIndex"),
|
|
18
|
-
]);
|
|
19
|
-
export const createEd25519Instruction = (message, instructionIndex, startingOffset) => {
|
|
20
|
-
const signatureOffset = startingOffset + MAGIC_LEN;
|
|
21
|
-
const publicKeyOffset = signatureOffset + SIGNATURE_LEN;
|
|
22
|
-
const messageDataSizeOffset = publicKeyOffset + PUBKEY_LEN;
|
|
23
|
-
const messageDataOffset = messageDataSizeOffset + MESSAGE_SIZE_LEN;
|
|
24
|
-
const messageDataSize = message.readUInt16LE(messageDataSizeOffset - startingOffset);
|
|
25
|
-
const instructionData = Buffer.alloc(ED25519_INSTRUCTION_LEN);
|
|
26
|
-
ED25519_INSTRUCTION_LAYOUT.encode({
|
|
27
|
-
numSignatures: 1,
|
|
28
|
-
padding: 0,
|
|
29
|
-
signatureOffset,
|
|
30
|
-
signatureInstructionIndex: instructionIndex,
|
|
31
|
-
publicKeyOffset,
|
|
32
|
-
publicKeyInstructionIndex: instructionIndex,
|
|
33
|
-
messageDataOffset,
|
|
34
|
-
messageDataSize: messageDataSize,
|
|
35
|
-
messageInstructionIndex: instructionIndex,
|
|
36
|
-
}, instructionData);
|
|
37
|
-
return new TransactionInstruction({
|
|
38
|
-
keys: [],
|
|
39
|
-
programId: Ed25519Program.programId,
|
|
40
|
-
data: instructionData,
|
|
41
|
-
});
|
|
42
|
-
};
|