@n1xyz/nord-ts 0.0.11 → 0.0.14
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/idl/bridge.js +33 -0
- package/dist/nord/api/core.d.ts +8 -8
- package/dist/nord/api/core.js +55 -14
- package/dist/nord/api/metrics.js +1 -1
- package/dist/nord/client/Nord.d.ts +39 -68
- package/dist/nord/client/Nord.js +97 -115
- package/dist/nord/client/NordUser.d.ts +0 -17
- package/dist/nord/client/NordUser.js +0 -28
- package/dist/types.d.ts +36 -13
- package/dist/types.js +2 -2
- package/dist/websocket/NordWebSocketClient.d.ts +0 -3
- package/dist/websocket/NordWebSocketClient.js +10 -13
- package/dist/websocket/events.d.ts +2 -2
- package/package.json +1 -1
- package/src/idl/bridge.ts +33 -0
- package/src/nord/api/core.ts +69 -17
- package/src/nord/api/metrics.ts +26 -20
- package/src/nord/api/queries.ts +7 -11
- package/src/nord/client/Nord.ts +126 -156
- package/src/nord/client/NordUser.ts +0 -49
- package/src/types.ts +38 -13
- package/src/websocket/NordWebSocketClient.ts +11 -14
- package/src/websocket/events.ts +2 -2
- package/test.ts +0 -107
|
@@ -64,15 +64,6 @@ export interface TransferParams {
|
|
|
64
64
|
/** Destination account ID */
|
|
65
65
|
toAccountId: number;
|
|
66
66
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Parameters for creating a new account
|
|
69
|
-
*/
|
|
70
|
-
export interface CreateAccountParams {
|
|
71
|
-
/** Token ID for initial funding */
|
|
72
|
-
tokenId: number;
|
|
73
|
-
/** Initial funding amount */
|
|
74
|
-
amount: Decimal.Value;
|
|
75
|
-
}
|
|
76
67
|
/**
|
|
77
68
|
* User class for interacting with the Nord protocol
|
|
78
69
|
*/
|
|
@@ -270,14 +261,6 @@ export declare class NordUser {
|
|
|
270
261
|
* @throws {NordError} If the operation fails
|
|
271
262
|
*/
|
|
272
263
|
transferToAccount(params: TransferParams): Promise<void>;
|
|
273
|
-
/**
|
|
274
|
-
* Create a new account
|
|
275
|
-
*
|
|
276
|
-
* @param params - Account creation parameters
|
|
277
|
-
* @returns New NordUser instance
|
|
278
|
-
* @throws {NordError} If the operation fails
|
|
279
|
-
*/
|
|
280
|
-
createAccount(params: CreateAccountParams): Promise<NordUser>;
|
|
281
264
|
/**
|
|
282
265
|
* Helper function to retry a promise with exponential backoff
|
|
283
266
|
*
|
|
@@ -547,34 +547,6 @@ class NordUser {
|
|
|
547
547
|
throw new NordError_1.NordError("Failed to transfer tokens", { cause: error });
|
|
548
548
|
}
|
|
549
549
|
}
|
|
550
|
-
/**
|
|
551
|
-
* Create a new account
|
|
552
|
-
*
|
|
553
|
-
* @param params - Account creation parameters
|
|
554
|
-
* @returns New NordUser instance
|
|
555
|
-
* @throws {NordError} If the operation fails
|
|
556
|
-
*/
|
|
557
|
-
async createAccount(params) {
|
|
558
|
-
try {
|
|
559
|
-
this.checkSessionValidity();
|
|
560
|
-
// Create a new keypair for the account
|
|
561
|
-
const keypair = web3_js_1.Keypair.generate();
|
|
562
|
-
// Create a new NordUser
|
|
563
|
-
const newUser = NordUser.fromPrivateKey(this.nord, keypair.secretKey, this.connection);
|
|
564
|
-
// Transfer initial funds
|
|
565
|
-
await this.transferToAccount({
|
|
566
|
-
to: newUser,
|
|
567
|
-
tokenId: params.tokenId,
|
|
568
|
-
amount: params.amount,
|
|
569
|
-
fromAccountId: (0, utils_2.optExpect)(this.accountIds?.[0], "No account ID"),
|
|
570
|
-
toAccountId: (0, utils_2.optExpect)(newUser.accountIds?.[0], "No account ID for new user"),
|
|
571
|
-
});
|
|
572
|
-
return newUser;
|
|
573
|
-
}
|
|
574
|
-
catch (error) {
|
|
575
|
-
throw new NordError_1.NordError("Failed to create account", { cause: error });
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
550
|
/**
|
|
579
551
|
* Helper function to retry a promise with exponential backoff
|
|
580
552
|
*
|
package/dist/types.d.ts
CHANGED
|
@@ -15,6 +15,16 @@ export declare enum PeakTpsPeriodUnit {
|
|
|
15
15
|
Month = "m",
|
|
16
16
|
Year = "y"
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Nord subscription type for trades or deltas
|
|
20
|
+
*/
|
|
21
|
+
export type SubscriptionType = 'trades' | 'deltas' | 'account';
|
|
22
|
+
/**
|
|
23
|
+
* Pattern for a valid Nord subscription
|
|
24
|
+
* Format should be: "<type>@<parameter>"
|
|
25
|
+
* Examples: "trades@BTCUSDC", "deltas@ETHUSDC", "account@42"
|
|
26
|
+
*/
|
|
27
|
+
export type SubscriptionPattern = `${SubscriptionType}@${string}` | string;
|
|
18
28
|
/**
|
|
19
29
|
* Configuration options for the Nord client
|
|
20
30
|
*/
|
|
@@ -27,11 +37,24 @@ export interface NordConfig {
|
|
|
27
37
|
solanaUrl: string;
|
|
28
38
|
/** Whether to initialize WebSockets on creation, defaults to true */
|
|
29
39
|
initWebSockets?: boolean;
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Initial subscriptions for the trades WebSocket
|
|
42
|
+
* Supports both formats:
|
|
43
|
+
* - Legacy format: ["BTCUSDC", "ETHUSDC"]
|
|
44
|
+
* - New format: ["trades@BTCUSDC", "trades@ETHUSDC"]
|
|
45
|
+
*/
|
|
46
|
+
tradesSubscriptions?: SubscriptionPattern[];
|
|
47
|
+
/**
|
|
48
|
+
* Initial subscriptions for the deltas WebSocket
|
|
49
|
+
* Supports both formats:
|
|
50
|
+
* - Legacy format: ["BTCUSDC", "ETHUSDC"]
|
|
51
|
+
* - New format: ["deltas@BTCUSDC", "deltas@ETHUSDC"]
|
|
52
|
+
*/
|
|
53
|
+
deltasSubscriptions?: SubscriptionPattern[];
|
|
34
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Configuration options for the Nord client
|
|
57
|
+
*/
|
|
35
58
|
export interface TokenInfo {
|
|
36
59
|
address: string;
|
|
37
60
|
precision: number;
|
|
@@ -355,22 +378,22 @@ export interface ActionNonceResponse {
|
|
|
355
378
|
export declare enum WebSocketMessageType {
|
|
356
379
|
Subscribe = "subscribe",
|
|
357
380
|
Unsubscribe = "unsubscribe",
|
|
358
|
-
TradeUpdate = "
|
|
381
|
+
TradeUpdate = "trades",
|
|
359
382
|
DeltaUpdate = "delta",
|
|
360
|
-
|
|
383
|
+
AccountUpdate = "account"
|
|
361
384
|
}
|
|
362
385
|
/**
|
|
363
386
|
* WebSocket subscription request
|
|
364
387
|
*/
|
|
365
388
|
export interface WebSocketSubscription {
|
|
366
|
-
|
|
389
|
+
e: WebSocketMessageType;
|
|
367
390
|
streams: string[];
|
|
368
391
|
}
|
|
369
392
|
/**
|
|
370
393
|
* WebSocket trade update message
|
|
371
394
|
*/
|
|
372
395
|
export interface WebSocketTradeUpdate {
|
|
373
|
-
|
|
396
|
+
e: WebSocketMessageType.TradeUpdate;
|
|
374
397
|
symbol: string;
|
|
375
398
|
trades: Trade[];
|
|
376
399
|
timestamp: number;
|
|
@@ -379,7 +402,7 @@ export interface WebSocketTradeUpdate {
|
|
|
379
402
|
* WebSocket delta update message
|
|
380
403
|
*/
|
|
381
404
|
export interface WebSocketDeltaUpdate {
|
|
382
|
-
|
|
405
|
+
e: WebSocketMessageType.DeltaUpdate;
|
|
383
406
|
last_update_id: number;
|
|
384
407
|
update_id: number;
|
|
385
408
|
market_symbol: string;
|
|
@@ -390,11 +413,11 @@ export interface WebSocketDeltaUpdate {
|
|
|
390
413
|
/**
|
|
391
414
|
* WebSocket user update message
|
|
392
415
|
*/
|
|
393
|
-
export interface
|
|
394
|
-
|
|
395
|
-
|
|
416
|
+
export interface WebSocketAccountUpdate {
|
|
417
|
+
e: WebSocketMessageType.AccountUpdate;
|
|
418
|
+
accountId: number;
|
|
396
419
|
account: Account;
|
|
397
420
|
timestamp: number;
|
|
398
421
|
}
|
|
399
|
-
export type WebSocketMessage = WebSocketSubscription | WebSocketTradeUpdate | WebSocketDeltaUpdate |
|
|
422
|
+
export type WebSocketMessage = WebSocketSubscription | WebSocketTradeUpdate | WebSocketDeltaUpdate | WebSocketAccountUpdate;
|
|
400
423
|
export {};
|
package/dist/types.js
CHANGED
|
@@ -97,7 +97,7 @@ var WebSocketMessageType;
|
|
|
97
97
|
(function (WebSocketMessageType) {
|
|
98
98
|
WebSocketMessageType["Subscribe"] = "subscribe";
|
|
99
99
|
WebSocketMessageType["Unsubscribe"] = "unsubscribe";
|
|
100
|
-
WebSocketMessageType["TradeUpdate"] = "
|
|
100
|
+
WebSocketMessageType["TradeUpdate"] = "trades";
|
|
101
101
|
WebSocketMessageType["DeltaUpdate"] = "delta";
|
|
102
|
-
WebSocketMessageType["
|
|
102
|
+
WebSocketMessageType["AccountUpdate"] = "account";
|
|
103
103
|
})(WebSocketMessageType || (exports.WebSocketMessageType = WebSocketMessageType = {}));
|
|
@@ -4,9 +4,6 @@ import { NordWebSocketClientEvents } from "./events";
|
|
|
4
4
|
* WebSocket client for Nord exchange
|
|
5
5
|
*
|
|
6
6
|
* This client connects to one of the specific Nord WebSocket endpoints:
|
|
7
|
-
* - /ws/trades - For trade updates
|
|
8
|
-
* - /ws/deltas - For orderbook delta updates
|
|
9
|
-
* - /ws/user - For user-specific updates
|
|
10
7
|
*
|
|
11
8
|
* Each endpoint handles a specific type of data and subscriptions must match
|
|
12
9
|
* the endpoint type (e.g., only 'trades@BTCUSDC' subscriptions are valid on
|
|
@@ -7,16 +7,13 @@ exports.NordWebSocketClient = void 0;
|
|
|
7
7
|
const ws_1 = __importDefault(require("ws"));
|
|
8
8
|
const events_1 = require("events");
|
|
9
9
|
const types_1 = require("../types");
|
|
10
|
-
const VALID_STREAM_TYPES = ["trades", "
|
|
10
|
+
const VALID_STREAM_TYPES = ["trades", "delta", "account"];
|
|
11
11
|
// Constants for WebSocket readyState
|
|
12
12
|
const WS_OPEN = 1;
|
|
13
13
|
/**
|
|
14
14
|
* WebSocket client for Nord exchange
|
|
15
15
|
*
|
|
16
16
|
* This client connects to one of the specific Nord WebSocket endpoints:
|
|
17
|
-
* - /ws/trades - For trade updates
|
|
18
|
-
* - /ws/deltas - For orderbook delta updates
|
|
19
|
-
* - /ws/user - For user-specific updates
|
|
20
17
|
*
|
|
21
18
|
* Each endpoint handles a specific type of data and subscriptions must match
|
|
22
19
|
* the endpoint type (e.g., only 'trades@BTCUSDC' subscriptions are valid on
|
|
@@ -64,8 +61,8 @@ class NordWebSocketClient extends events_1.EventEmitter {
|
|
|
64
61
|
if (!VALID_STREAM_TYPES.includes(type)) {
|
|
65
62
|
throw new Error(`Invalid stream type: ${type}. Valid types are: ${VALID_STREAM_TYPES.join(", ")}`);
|
|
66
63
|
}
|
|
67
|
-
if (type === "
|
|
68
|
-
throw new Error(`Invalid
|
|
64
|
+
if (type === "account" && !/^\d+$/.test(params)) {
|
|
65
|
+
throw new Error(`Invalid account ID in stream: ${params}. Expected numeric ID`);
|
|
69
66
|
}
|
|
70
67
|
}
|
|
71
68
|
/**
|
|
@@ -228,7 +225,7 @@ class NordWebSocketClient extends events_1.EventEmitter {
|
|
|
228
225
|
return;
|
|
229
226
|
}
|
|
230
227
|
const message = {
|
|
231
|
-
|
|
228
|
+
e: types_1.WebSocketMessageType.Subscribe,
|
|
232
229
|
streams,
|
|
233
230
|
};
|
|
234
231
|
try {
|
|
@@ -266,7 +263,7 @@ class NordWebSocketClient extends events_1.EventEmitter {
|
|
|
266
263
|
return;
|
|
267
264
|
}
|
|
268
265
|
const message = {
|
|
269
|
-
|
|
266
|
+
e: types_1.WebSocketMessageType.Unsubscribe,
|
|
270
267
|
streams,
|
|
271
268
|
};
|
|
272
269
|
try {
|
|
@@ -311,18 +308,18 @@ class NordWebSocketClient extends events_1.EventEmitter {
|
|
|
311
308
|
* @param message WebSocket message
|
|
312
309
|
*/
|
|
313
310
|
handleMessage(message) {
|
|
314
|
-
switch (message.
|
|
311
|
+
switch (message.e) {
|
|
315
312
|
case types_1.WebSocketMessageType.TradeUpdate:
|
|
316
|
-
this.emit("
|
|
313
|
+
this.emit("trades", message);
|
|
317
314
|
break;
|
|
318
315
|
case types_1.WebSocketMessageType.DeltaUpdate:
|
|
319
316
|
this.emit("delta", message);
|
|
320
317
|
break;
|
|
321
|
-
case types_1.WebSocketMessageType.
|
|
322
|
-
this.emit("
|
|
318
|
+
case types_1.WebSocketMessageType.AccountUpdate:
|
|
319
|
+
this.emit("account", message);
|
|
323
320
|
break;
|
|
324
321
|
default:
|
|
325
|
-
this.emit("error", new Error(`Unknown message type: ${message.
|
|
322
|
+
this.emit("error", new Error(`Unknown message type: ${message.e}`));
|
|
326
323
|
}
|
|
327
324
|
}
|
|
328
325
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WebSocketTradeUpdate, WebSocketDeltaUpdate,
|
|
1
|
+
import { WebSocketTradeUpdate, WebSocketDeltaUpdate, WebSocketAccountUpdate } from "../types";
|
|
2
2
|
/**
|
|
3
3
|
* Event type definitions for the NordWebSocketClient
|
|
4
4
|
*/
|
|
@@ -8,7 +8,7 @@ export interface NordWebSocketEvents {
|
|
|
8
8
|
error: (error: Error) => void;
|
|
9
9
|
trade: (update: WebSocketTradeUpdate) => void;
|
|
10
10
|
delta: (update: WebSocketDeltaUpdate) => void;
|
|
11
|
-
|
|
11
|
+
account: (update: WebSocketAccountUpdate) => void;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* Type declaration for NordWebSocketClient event methods
|
package/package.json
CHANGED
package/src/idl/bridge.ts
CHANGED
|
@@ -110,6 +110,10 @@ export const BRIDGE_IDL: Idl = {
|
|
|
110
110
|
},
|
|
111
111
|
},
|
|
112
112
|
},
|
|
113
|
+
{
|
|
114
|
+
name: "program",
|
|
115
|
+
address: "AAqdriZHrKLurrGRecnCEHKqZLKmq6Q2mFmH26i3ff3r",
|
|
116
|
+
},
|
|
113
117
|
{
|
|
114
118
|
name: "token_program",
|
|
115
119
|
},
|
|
@@ -195,6 +199,10 @@ export const BRIDGE_IDL: Idl = {
|
|
|
195
199
|
],
|
|
196
200
|
},
|
|
197
201
|
},
|
|
202
|
+
{
|
|
203
|
+
name: "program",
|
|
204
|
+
address: "AAqdriZHrKLurrGRecnCEHKqZLKmq6Q2mFmH26i3ff3r",
|
|
205
|
+
},
|
|
198
206
|
{
|
|
199
207
|
name: "system_program",
|
|
200
208
|
address: "11111111111111111111111111111111",
|
|
@@ -249,6 +257,10 @@ export const BRIDGE_IDL: Idl = {
|
|
|
249
257
|
],
|
|
250
258
|
},
|
|
251
259
|
},
|
|
260
|
+
{
|
|
261
|
+
name: "program",
|
|
262
|
+
address: "AAqdriZHrKLurrGRecnCEHKqZLKmq6Q2mFmH26i3ff3r",
|
|
263
|
+
},
|
|
252
264
|
{
|
|
253
265
|
name: "system_program",
|
|
254
266
|
address: "11111111111111111111111111111111",
|
|
@@ -272,6 +284,11 @@ export const BRIDGE_IDL: Idl = {
|
|
|
272
284
|
writable: true,
|
|
273
285
|
signer: true,
|
|
274
286
|
},
|
|
287
|
+
{
|
|
288
|
+
name: "program",
|
|
289
|
+
signer: true,
|
|
290
|
+
address: "AAqdriZHrKLurrGRecnCEHKqZLKmq6Q2mFmH26i3ff3r",
|
|
291
|
+
},
|
|
275
292
|
{
|
|
276
293
|
name: "contract_storage",
|
|
277
294
|
writable: true,
|
|
@@ -359,6 +376,10 @@ export const BRIDGE_IDL: Idl = {
|
|
|
359
376
|
],
|
|
360
377
|
},
|
|
361
378
|
},
|
|
379
|
+
{
|
|
380
|
+
name: "program",
|
|
381
|
+
address: "AAqdriZHrKLurrGRecnCEHKqZLKmq6Q2mFmH26i3ff3r",
|
|
382
|
+
},
|
|
362
383
|
{
|
|
363
384
|
name: "system_program",
|
|
364
385
|
address: "11111111111111111111111111111111",
|
|
@@ -415,6 +436,10 @@ export const BRIDGE_IDL: Idl = {
|
|
|
415
436
|
],
|
|
416
437
|
},
|
|
417
438
|
},
|
|
439
|
+
{
|
|
440
|
+
name: "program",
|
|
441
|
+
address: "AAqdriZHrKLurrGRecnCEHKqZLKmq6Q2mFmH26i3ff3r",
|
|
442
|
+
},
|
|
418
443
|
],
|
|
419
444
|
args: [
|
|
420
445
|
{
|
|
@@ -468,6 +493,10 @@ export const BRIDGE_IDL: Idl = {
|
|
|
468
493
|
],
|
|
469
494
|
},
|
|
470
495
|
},
|
|
496
|
+
{
|
|
497
|
+
name: "program",
|
|
498
|
+
address: "AAqdriZHrKLurrGRecnCEHKqZLKmq6Q2mFmH26i3ff3r",
|
|
499
|
+
},
|
|
471
500
|
{
|
|
472
501
|
name: "system_program",
|
|
473
502
|
address: "11111111111111111111111111111111",
|
|
@@ -594,6 +623,10 @@ export const BRIDGE_IDL: Idl = {
|
|
|
594
623
|
],
|
|
595
624
|
},
|
|
596
625
|
},
|
|
626
|
+
{
|
|
627
|
+
name: "program",
|
|
628
|
+
address: "AAqdriZHrKLurrGRecnCEHKqZLKmq6Q2mFmH26i3ff3r",
|
|
629
|
+
},
|
|
597
630
|
{
|
|
598
631
|
name: "token_program",
|
|
599
632
|
},
|
package/src/nord/api/core.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Account, Info } from "../../types";
|
|
1
|
+
import { Account, Info, SubscriptionPattern } from "../../types";
|
|
2
2
|
import { checkedFetch } from "../../utils";
|
|
3
3
|
import { NordWebSocketClient } from "../../websocket/index";
|
|
4
4
|
import { NordError } from "../utils/NordError";
|
|
@@ -77,27 +77,55 @@ export async function getAccount(
|
|
|
77
77
|
/**
|
|
78
78
|
* Initialize a WebSocket client for Nord
|
|
79
79
|
*
|
|
80
|
-
* Connects to
|
|
81
|
-
* -
|
|
82
|
-
* -
|
|
83
|
-
* -
|
|
80
|
+
* Connects to the Nord WebSocket endpoint with support for multiple subscription types:
|
|
81
|
+
* - trades@SYMBOL - For trade updates
|
|
82
|
+
* - deltas@SYMBOL - For orderbook delta updates
|
|
83
|
+
* - account@ACCOUNT_ID - For user-specific updates
|
|
84
84
|
*
|
|
85
85
|
* @param webServerUrl - Base URL for the Nord web server
|
|
86
|
-
* @param
|
|
87
|
-
* @param initialSubscriptions - Optional array of initial subscriptions (e.g., ["trades@BTCUSDC"])
|
|
86
|
+
* @param subscriptions - Array of subscriptions (e.g., ["trades@BTCUSDC", "deltas@BTCUSDC", "account@42"])
|
|
88
87
|
* @returns WebSocket client
|
|
88
|
+
* @throws {NordError} If initialization fails or invalid subscription is provided
|
|
89
89
|
*/
|
|
90
90
|
export function initWebSocketClient(
|
|
91
91
|
webServerUrl: string,
|
|
92
|
-
|
|
93
|
-
initialSubscriptions?:
|
|
92
|
+
subscriptions?: SubscriptionPattern[] | "trades" | "delta" | "account",
|
|
93
|
+
initialSubscriptions?: SubscriptionPattern[],
|
|
94
94
|
): NordWebSocketClient {
|
|
95
95
|
try {
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
// Determine URL and subscriptions based on parameters
|
|
97
|
+
let wsUrl = webServerUrl.replace(/^http/, "ws") + `/ws`;
|
|
98
|
+
let wsSubscriptions: SubscriptionPattern[] = [];
|
|
99
|
+
|
|
100
|
+
// Validate subscriptions parameter
|
|
101
|
+
if (typeof subscriptions === "string") {
|
|
102
|
+
// Legacy mode - handle endpoint string
|
|
103
|
+
if (
|
|
104
|
+
subscriptions === "trades" ||
|
|
105
|
+
subscriptions === "delta" ||
|
|
106
|
+
subscriptions === "account"
|
|
107
|
+
) {
|
|
108
|
+
wsUrl += `/${subscriptions}`;
|
|
109
|
+
// If initialSubscriptions provided, use them
|
|
110
|
+
if (initialSubscriptions && initialSubscriptions.length > 0) {
|
|
111
|
+
// Validate initialSubscriptions
|
|
112
|
+
initialSubscriptions.forEach(validateSubscription);
|
|
113
|
+
wsSubscriptions = initialSubscriptions;
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
throw new NordError(
|
|
117
|
+
`Invalid endpoint: ${subscriptions}. Must be "trades", "deltas", or "account".`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
} else if (Array.isArray(subscriptions) && subscriptions.length > 0) {
|
|
121
|
+
// New mode - validate and combine subscriptions in URL
|
|
122
|
+
subscriptions.forEach(validateSubscription);
|
|
123
|
+
wsUrl += `/${subscriptions.join("&")}`;
|
|
124
|
+
} else {
|
|
125
|
+
// Default to trades endpoint if no subscriptions specified
|
|
126
|
+
wsUrl += `/trades`;
|
|
127
|
+
}
|
|
128
|
+
|
|
101
129
|
console.log(`Initializing WebSocket client with URL: ${wsUrl}`);
|
|
102
130
|
|
|
103
131
|
// Create and connect the WebSocket client
|
|
@@ -112,9 +140,10 @@ export function initWebSocketClient(
|
|
|
112
140
|
ws.on("connected", () => {
|
|
113
141
|
console.log("Nord WebSocket connected successfully");
|
|
114
142
|
|
|
115
|
-
// Subscribe to
|
|
116
|
-
|
|
117
|
-
|
|
143
|
+
// Subscribe to additional subscriptions if provided
|
|
144
|
+
// For new format, these are already part of the URL
|
|
145
|
+
if (wsSubscriptions.length > 0) {
|
|
146
|
+
ws.subscribe(wsSubscriptions);
|
|
118
147
|
}
|
|
119
148
|
});
|
|
120
149
|
|
|
@@ -128,3 +157,26 @@ export function initWebSocketClient(
|
|
|
128
157
|
});
|
|
129
158
|
}
|
|
130
159
|
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Validates a subscription string follows the correct format
|
|
163
|
+
*
|
|
164
|
+
* @param subscription - The subscription to validate
|
|
165
|
+
* @throws {NordError} If the subscription format is invalid
|
|
166
|
+
*/
|
|
167
|
+
function validateSubscription(subscription: string): void {
|
|
168
|
+
const [type, param] = subscription.split("@");
|
|
169
|
+
|
|
170
|
+
if (!type || !param || !["trades", "deltas", "account"].includes(type)) {
|
|
171
|
+
throw new NordError(
|
|
172
|
+
`Invalid subscription format: ${subscription}. Expected format: "trades@SYMBOL", "deltas@SYMBOL", or "account@ID"`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Additional validation for account subscriptions
|
|
177
|
+
if (type === "account" && isNaN(Number(param))) {
|
|
178
|
+
throw new NordError(
|
|
179
|
+
`Invalid account ID in subscription: ${subscription}. Account ID must be a number.`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
}
|
package/src/nord/api/metrics.ts
CHANGED
|
@@ -33,19 +33,19 @@ export async function aggregateMetrics(
|
|
|
33
33
|
const response = await checkedFetch(
|
|
34
34
|
`${webServerUrl}/metrics?tx_peak_tps_period=${txPeakTpsPeriod}&tx_peak_tps_period_unit=${txPeakTpsPeriodUnit}`,
|
|
35
35
|
);
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
// Get the raw text response (Prometheus format)
|
|
38
38
|
const text = await response.text();
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
// Parse the Prometheus-formatted metrics text into an AggregateMetrics object
|
|
41
41
|
const metrics: AggregateMetrics = {
|
|
42
42
|
blocks_total: 0,
|
|
43
43
|
tx_total: extractMetricValue(text, "nord_requests_ok_count"),
|
|
44
44
|
tx_tps: calculateTps(text),
|
|
45
45
|
tx_tps_peak: calculatePeakTps(text),
|
|
46
|
-
request_latency_average: extractLatency(text)
|
|
46
|
+
request_latency_average: extractLatency(text),
|
|
47
47
|
};
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
return metrics;
|
|
50
50
|
} catch (error) {
|
|
51
51
|
throw new NordError("Failed to fetch aggregate metrics", { cause: error });
|
|
@@ -54,20 +54,20 @@ export async function aggregateMetrics(
|
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
56
|
* Extract a metric value from Prometheus-formatted text
|
|
57
|
-
*
|
|
57
|
+
*
|
|
58
58
|
* @param text - Prometheus-formatted metrics text
|
|
59
59
|
* @param metricName - Name of the metric to extract
|
|
60
60
|
* @returns The metric value as a number, or 0 if not found
|
|
61
61
|
*/
|
|
62
62
|
function extractMetricValue(text: string, metricName: string): number {
|
|
63
|
-
const regex = new RegExp(`^${metricName}\\s+([\\d.]+)`,
|
|
63
|
+
const regex = new RegExp(`^${metricName}\\s+([\\d.]+)`, "m");
|
|
64
64
|
const match = text.match(regex);
|
|
65
65
|
return match ? parseFloat(match[1]) : 0;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
69
|
* Calculate TPS from Prometheus metrics
|
|
70
|
-
*
|
|
70
|
+
*
|
|
71
71
|
* @param text - Prometheus-formatted metrics text
|
|
72
72
|
* @returns Calculated TPS value
|
|
73
73
|
*/
|
|
@@ -75,22 +75,25 @@ function calculateTps(text: string): number {
|
|
|
75
75
|
// Use the request count and latency to estimate TPS
|
|
76
76
|
const requestCount = extractMetricValue(text, "nord_requests_ok_count");
|
|
77
77
|
const latencySum = extractSummaryValue(text, "nord_requests_ok_latency_sum");
|
|
78
|
-
const latencyCount = extractSummaryValue(
|
|
79
|
-
|
|
78
|
+
const latencyCount = extractSummaryValue(
|
|
79
|
+
text,
|
|
80
|
+
"nord_requests_ok_latency_count",
|
|
81
|
+
);
|
|
82
|
+
|
|
80
83
|
if (latencySum > 0 && latencyCount > 0) {
|
|
81
84
|
// Average latency in seconds
|
|
82
85
|
const avgLatency = latencySum / latencyCount;
|
|
83
86
|
// If we have valid latency data, estimate TPS as requests per second
|
|
84
87
|
return avgLatency > 0 ? requestCount / (latencyCount * avgLatency) : 0;
|
|
85
88
|
}
|
|
86
|
-
|
|
89
|
+
|
|
87
90
|
// Fallback: just return a small fraction of the total request count
|
|
88
91
|
return requestCount > 0 ? requestCount / 100 : 0;
|
|
89
92
|
}
|
|
90
93
|
|
|
91
94
|
/**
|
|
92
95
|
* Calculate peak TPS from Prometheus metrics
|
|
93
|
-
*
|
|
96
|
+
*
|
|
94
97
|
* @param text - Prometheus-formatted metrics text
|
|
95
98
|
* @returns Calculated peak TPS value
|
|
96
99
|
*/
|
|
@@ -101,26 +104,29 @@ function calculatePeakTps(text: string): number {
|
|
|
101
104
|
|
|
102
105
|
/**
|
|
103
106
|
* Extract latency from Prometheus metrics
|
|
104
|
-
*
|
|
107
|
+
*
|
|
105
108
|
* @param text - Prometheus-formatted metrics text
|
|
106
109
|
* @returns Average latency in seconds
|
|
107
110
|
*/
|
|
108
111
|
function extractLatency(text: string): number {
|
|
109
112
|
const latencySum = extractSummaryValue(text, "nord_requests_ok_latency_sum");
|
|
110
|
-
const latencyCount = extractSummaryValue(
|
|
111
|
-
|
|
113
|
+
const latencyCount = extractSummaryValue(
|
|
114
|
+
text,
|
|
115
|
+
"nord_requests_ok_latency_count",
|
|
116
|
+
);
|
|
117
|
+
|
|
112
118
|
return latencyCount > 0 ? latencySum / latencyCount : 0;
|
|
113
119
|
}
|
|
114
120
|
|
|
115
121
|
/**
|
|
116
122
|
* Extract a summary value from Prometheus-formatted text
|
|
117
|
-
*
|
|
123
|
+
*
|
|
118
124
|
* @param text - Prometheus-formatted metrics text
|
|
119
125
|
* @param metricName - Name of the metric to extract
|
|
120
126
|
* @returns The metric value as a number, or 0 if not found
|
|
121
127
|
*/
|
|
122
128
|
function extractSummaryValue(text: string, metricName: string): number {
|
|
123
|
-
const regex = new RegExp(`^${metricName}\\s+([\\d.]+)`,
|
|
129
|
+
const regex = new RegExp(`^${metricName}\\s+([\\d.]+)`, "m");
|
|
124
130
|
const match = text.match(regex);
|
|
125
131
|
return match ? parseFloat(match[1]) : 0;
|
|
126
132
|
}
|
|
@@ -235,21 +241,21 @@ export async function queryPrometheus(
|
|
|
235
241
|
const response = await checkedFetch(
|
|
236
242
|
`${webServerUrl}/prometheus?query=${encodeURIComponent(params)}`,
|
|
237
243
|
);
|
|
238
|
-
|
|
244
|
+
|
|
239
245
|
// Handle raw text response
|
|
240
246
|
const text = await response.text();
|
|
241
247
|
try {
|
|
242
248
|
// Try to parse as JSON first
|
|
243
249
|
const data = JSON.parse(text);
|
|
244
250
|
return data.data.result[0]?.value[1] || 0;
|
|
245
|
-
} catch (
|
|
246
|
-
|
|
251
|
+
} catch (error) {
|
|
252
|
+
console.log("Prometheus query failed:", error);
|
|
247
253
|
// Try to find a number in the response
|
|
248
254
|
const numberMatch = text.match(/[\d.]+/);
|
|
249
255
|
if (numberMatch) {
|
|
250
256
|
return parseFloat(numberMatch[0]);
|
|
251
257
|
}
|
|
252
|
-
|
|
258
|
+
|
|
253
259
|
// Return 0 if no number is found
|
|
254
260
|
return 0;
|
|
255
261
|
}
|
package/src/nord/api/queries.ts
CHANGED
|
@@ -2,17 +2,12 @@ import {
|
|
|
2
2
|
ActionQuery,
|
|
3
3
|
ActionResponse,
|
|
4
4
|
ActionsResponse,
|
|
5
|
-
BlockQuery,
|
|
6
|
-
BlockResponse,
|
|
7
|
-
BlockSummaryResponse,
|
|
8
5
|
RollmanActionResponse,
|
|
9
6
|
RollmanActionsResponse,
|
|
10
|
-
RollmanBlockResponse,
|
|
11
7
|
} from "../../types";
|
|
12
8
|
import { checkedFetch } from "../../utils";
|
|
13
9
|
import { NordError } from "../utils/NordError";
|
|
14
10
|
|
|
15
|
-
|
|
16
11
|
/**
|
|
17
12
|
* Query a specific action
|
|
18
13
|
*
|
|
@@ -60,9 +55,12 @@ export async function queryRecentActions(
|
|
|
60
55
|
);
|
|
61
56
|
return await response.json();
|
|
62
57
|
} catch (error) {
|
|
63
|
-
throw new NordError(
|
|
64
|
-
|
|
65
|
-
|
|
58
|
+
throw new NordError(
|
|
59
|
+
`Failed to query recent actions (from ${from} to ${to})`,
|
|
60
|
+
{
|
|
61
|
+
cause: error,
|
|
62
|
+
},
|
|
63
|
+
);
|
|
66
64
|
}
|
|
67
65
|
}
|
|
68
66
|
|
|
@@ -73,9 +71,7 @@ export async function queryRecentActions(
|
|
|
73
71
|
* @returns Last action ID
|
|
74
72
|
* @throws {NordError} If the request fails
|
|
75
73
|
*/
|
|
76
|
-
export async function getLastActionId(
|
|
77
|
-
webServerUrl: string,
|
|
78
|
-
): Promise<number> {
|
|
74
|
+
export async function getLastActionId(webServerUrl: string): Promise<number> {
|
|
79
75
|
try {
|
|
80
76
|
const response = await checkedFetch(`${webServerUrl}/last_actionid`);
|
|
81
77
|
const data = await response.json();
|