@n1xyz/nord-ts 0.0.19 → 0.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/api/client.d.ts +14 -0
  2. package/dist/api/client.js +45 -0
  3. package/dist/bridge/client.d.ts +151 -0
  4. package/dist/bridge/client.js +434 -0
  5. package/dist/bridge/const.d.ts +23 -0
  6. package/dist/bridge/const.js +47 -0
  7. package/dist/bridge/index.d.ts +4 -0
  8. package/dist/bridge/index.js +23 -0
  9. package/dist/bridge/types.d.ts +120 -0
  10. package/dist/bridge/types.js +18 -0
  11. package/dist/bridge/utils.d.ts +64 -0
  12. package/dist/bridge/utils.js +131 -0
  13. package/dist/const.d.ts +8 -0
  14. package/dist/const.js +30 -0
  15. package/dist/gen/common.d.ts +68 -0
  16. package/dist/gen/common.js +215 -0
  17. package/dist/gen/nord.d.ts +882 -0
  18. package/dist/gen/nord.js +6520 -0
  19. package/dist/gen/openapi.d.ts +2244 -0
  20. package/dist/gen/openapi.js +6 -0
  21. package/dist/idl/bridge.d.ts +569 -0
  22. package/dist/idl/bridge.js +8 -0
  23. package/dist/idl/bridge.json +1506 -0
  24. package/dist/idl/index.d.ts +607 -0
  25. package/dist/idl/index.js +8 -0
  26. package/dist/index.d.ts +5 -0
  27. package/dist/index.js +21 -0
  28. package/dist/nord/api/actions.d.ts +135 -0
  29. package/dist/nord/api/actions.js +313 -0
  30. package/dist/nord/api/core.d.ts +16 -0
  31. package/dist/nord/api/core.js +93 -0
  32. package/dist/nord/api/market.d.ts +36 -0
  33. package/dist/nord/api/market.js +96 -0
  34. package/dist/nord/api/metrics.d.ts +67 -0
  35. package/dist/nord/api/metrics.js +229 -0
  36. package/dist/nord/api/queries.d.ts +46 -0
  37. package/dist/nord/api/queries.js +109 -0
  38. package/dist/nord/client/Nord.d.ts +282 -0
  39. package/dist/nord/client/Nord.js +531 -0
  40. package/dist/nord/client/NordUser.d.ts +340 -0
  41. package/dist/nord/client/NordUser.js +652 -0
  42. package/dist/nord/index.d.ts +7 -0
  43. package/dist/nord/index.js +31 -0
  44. package/dist/nord/models/Subscriber.d.ts +37 -0
  45. package/dist/nord/models/Subscriber.js +25 -0
  46. package/dist/nord/utils/NordError.d.ts +35 -0
  47. package/dist/nord/utils/NordError.js +49 -0
  48. package/dist/types.d.ts +260 -0
  49. package/dist/types.js +103 -0
  50. package/dist/utils.d.ts +98 -0
  51. package/dist/utils.js +237 -0
  52. package/dist/websocket/NordWebSocketClient.d.ts +68 -0
  53. package/dist/websocket/NordWebSocketClient.js +343 -0
  54. package/dist/websocket/events.d.ts +19 -0
  55. package/dist/websocket/events.js +2 -0
  56. package/dist/websocket/index.d.ts +2 -0
  57. package/dist/websocket/index.js +5 -0
  58. package/package.json +7 -1
  59. package/src/gen/.gitkeep +0 -0
  60. package/src/gen/nord.ts +7593 -0
  61. package/src/gen/openapi.ts +2245 -0
  62. package/.claude/settings.local.json +0 -11
  63. package/.local/qa.ts +0 -77
  64. package/.local/test-atomic.ts +0 -112
  65. package/.prettierignore +0 -2
  66. package/check.sh +0 -4
  67. package/default.nix +0 -47
  68. package/eslint.config.mjs +0 -66
  69. package/tests/utils.spec.ts +0 -121
  70. package/tsconfig.eslint.json +0 -12
  71. package/tsconfig.json +0 -24
@@ -0,0 +1,531 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.Nord = void 0;
40
+ const events_1 = require("events");
41
+ const web3_js_1 = require("@solana/web3.js");
42
+ const openapi_fetch_1 = __importDefault(require("openapi-fetch"));
43
+ const types_1 = require("../../types");
44
+ const proton_1 = require("@n1xyz/proton");
45
+ const proto = __importStar(require("../../gen/nord"));
46
+ const core = __importStar(require("../api/core"));
47
+ const metrics = __importStar(require("../api/metrics"));
48
+ const utils = __importStar(require("../../utils"));
49
+ const NordError_1 = require("../utils/NordError");
50
+ /**
51
+ * Main Nord client class for interacting with the Nord API
52
+ */
53
+ class Nord {
54
+ /**
55
+ * Create a new Nord client
56
+ *
57
+ * @param config - Configuration options for the Nord client
58
+ * @param config.webServerUrl - Base URL for the Nord web server
59
+ * @param config.bridgeVk - Bridge verification key
60
+ * @param config.solanaUrl - Solana cluster URL
61
+ * @throws {Error} If required configuration is missing
62
+ */
63
+ constructor({ bridgeVk, solanaUrl, webServerUrl, protonClient, }) {
64
+ /** Available markets */
65
+ this.markets = [];
66
+ /** Available tokens */
67
+ this.tokens = [];
68
+ /** Map of symbol to market_id */
69
+ this.symbolToMarketId = new Map();
70
+ this.webServerUrl = webServerUrl;
71
+ this.bridgeVk = bridgeVk;
72
+ this.solanaUrl = solanaUrl;
73
+ this.protonClient = protonClient;
74
+ this.httpClient = (0, openapi_fetch_1.default)({ baseUrl: webServerUrl });
75
+ }
76
+ /**
77
+ * Create a WebSocket client with specific subscriptions
78
+ *
79
+ * @param options - Subscription options that specify which data streams to subscribe to
80
+ * @returns A new WebSocket client with the requested subscriptions
81
+ * @throws {NordError} If invalid subscription options are provided
82
+ *
83
+ * @example
84
+ * // Create a client for trades and deltas from one market and an account
85
+ * const wsClient = nord.createWebSocketClient({
86
+ * trades: ["BTCUSDC"],
87
+ * deltas: ["BTCUSDC"],
88
+ * accounts: [123]
89
+ * });
90
+ *
91
+ * @example
92
+ * // Create a client for trades from multiple markets
93
+ * const tradesClient = nord.createWebSocketClient({
94
+ * trades: ["BTCUSDC", "ETHUSDC"]
95
+ * });
96
+ */
97
+ createWebSocketClient(options) {
98
+ const subscriptions = [];
99
+ // Add trade subscriptions
100
+ if (options.trades && options.trades.length > 0) {
101
+ options.trades.forEach((symbol) => {
102
+ subscriptions.push(`trades@${symbol}`);
103
+ });
104
+ }
105
+ // Add delta subscriptions
106
+ if (options.deltas && options.deltas.length > 0) {
107
+ options.deltas.forEach((symbol) => {
108
+ subscriptions.push(`deltas@${symbol}`);
109
+ });
110
+ }
111
+ // Add account subscriptions
112
+ if (options.accounts && options.accounts.length > 0) {
113
+ options.accounts.forEach((accountId) => {
114
+ if (isNaN(accountId) || accountId <= 0) {
115
+ throw new NordError_1.NordError(`Invalid account ID: ${accountId}. Must be a positive number.`);
116
+ }
117
+ subscriptions.push(`account@${accountId}`);
118
+ });
119
+ }
120
+ // Validate that at least one subscription was provided
121
+ if (subscriptions.length === 0) {
122
+ throw new NordError_1.NordError("At least one subscription must be provided");
123
+ }
124
+ // Create and return a new WebSocket client
125
+ return core.initWebSocketClient(this.webServerUrl, subscriptions);
126
+ }
127
+ async GET(path, options) {
128
+ const r = await this.httpClient.GET(path, options);
129
+ if (r.error) {
130
+ throw new NordError_1.NordError(`failed to GET ${path}`, { cause: r.error });
131
+ }
132
+ if (r.data === undefined) {
133
+ // this should never happen, but the type checker seems unhappy.
134
+ // if we catch this we'll need to debug accordingly.
135
+ throw new NordError_1.NordError("internal assertion violation", { cause: r });
136
+ }
137
+ return r.data;
138
+ }
139
+ /**
140
+ * Get the current timestamp from the Nord server
141
+ *
142
+ * @returns Current timestamp as a bigint
143
+ * @throws {NordError} If the request fails
144
+ */
145
+ async getTimestamp() {
146
+ return BigInt(await this.GET("/timestamp", {}));
147
+ }
148
+ /**
149
+ * Get the last event nonce from the Nord server
150
+ *
151
+ * @returns Next action nonce
152
+ * @throws {NordError} If the request fails
153
+ */
154
+ async getActionNonce() {
155
+ return await this.GET("/event/last-acked-nonce", {});
156
+ }
157
+ /**
158
+ * Fetch information about Nord markets and tokens
159
+ *
160
+ * @throws {NordError} If the request fails
161
+ */
162
+ async fetchNordInfo() {
163
+ try {
164
+ const info = await this.GET("/info", {});
165
+ this.markets = info.markets;
166
+ this.tokens = info.tokens;
167
+ // Populate the symbolToMarketId map
168
+ this.symbolToMarketId.clear();
169
+ info.markets.forEach((market) => {
170
+ this.symbolToMarketId.set(market.symbol, market.marketId);
171
+ });
172
+ }
173
+ catch (error) {
174
+ throw new NordError_1.NordError("Failed to fetch Nord info", { cause: error });
175
+ }
176
+ }
177
+ /**
178
+ * Initialize a new Nord client
179
+ *
180
+ * @param nordConfig - Configuration options for the Nord client
181
+ * @param nordConfig.webServerUrl - Base URL for the Nord web server
182
+ * @param nordConfig.bridgeVk - Bridge verification key
183
+ * @param nordConfig.solanaUrl - Solana cluster URL
184
+ * @returns Initialized Nord client
185
+ * @throws {NordError} If initialization fails
186
+ */
187
+ static async initNord({ bridgeVk: bridgeVk_, solanaUrl, webServerUrl, }) {
188
+ // TODO: we should parametrize the connectionn not have it done here.
189
+ // this is a dogshit api, only here to be compatible with the shitty
190
+ // vibecoded code and not break zero one team's workflow.
191
+ const connection = new web3_js_1.Connection(solanaUrl, { commitment: "confirmed" });
192
+ const bridgeVk = new web3_js_1.PublicKey(bridgeVk_);
193
+ const protonClient = await proton_1.ProtonClient.init({
194
+ protonUrl: webServerUrl,
195
+ bridgeVk,
196
+ solConn: connection,
197
+ });
198
+ const nord = new Nord({
199
+ bridgeVk,
200
+ protonClient,
201
+ solanaUrl,
202
+ webServerUrl,
203
+ });
204
+ await nord.init();
205
+ return nord;
206
+ }
207
+ /**
208
+ * Initialize the Nord client
209
+ * @private
210
+ */
211
+ async init() {
212
+ await this.fetchNordInfo();
213
+ }
214
+ /**
215
+ * Query a specific action
216
+ *
217
+ * @param query - Action query parameters
218
+ * @returns Action response
219
+ * @throws {NordError} If the request fails
220
+ */
221
+ async queryAction({ action_id, }) {
222
+ return ((await this.queryRecentActions({
223
+ from: action_id,
224
+ to: action_id,
225
+ }))[0] ?? null);
226
+ }
227
+ /**
228
+ * Query recent actions
229
+ *
230
+ * @param from - Starting action index
231
+ * @param to - Ending action index
232
+ * @returns Actions response
233
+ * @throws {NordError} If the request fails
234
+ */
235
+ async queryRecentActions(query) {
236
+ const xs = await this.GET("/action", {
237
+ params: {
238
+ query,
239
+ },
240
+ });
241
+ return xs.map((x) => ({
242
+ actionId: x.actionId,
243
+ action: utils.decodeLengthDelimited(Buffer.from(x.payload, "base64"), proto.Action),
244
+ physicalExecTime: new Date(x.physicalTime * 1000),
245
+ }));
246
+ }
247
+ /**
248
+ * Get the last action ID
249
+ *
250
+ * @returns Last action ID
251
+ * @throws {NordError} If the request fails
252
+ */
253
+ async getLastActionId() {
254
+ return await this.GET("/action/last-executed-id", {});
255
+ }
256
+ /**
257
+ * Fetch aggregate metrics from the Nord API
258
+ *
259
+ * @param txPeakTpsPeriod - Period for peak TPS calculation
260
+ * @param txPeakTpsPeriodUnit - Unit for peak TPS period
261
+ * @returns Aggregate metrics
262
+ * @throws {NordError} If the request fails
263
+ */
264
+ async aggregateMetrics(txPeakTpsPeriod = 1, txPeakTpsPeriodUnit = types_1.PeakTpsPeriodUnit.Day) {
265
+ return metrics.aggregateMetrics(this.webServerUrl, txPeakTpsPeriod, txPeakTpsPeriodUnit);
266
+ }
267
+ /**
268
+ * Get current transactions per second
269
+ *
270
+ * @param period - Time period for the query
271
+ * @returns Current TPS value
272
+ * @throws {NordError} If the request fails
273
+ */
274
+ async getCurrentTps(period = "1m") {
275
+ return metrics.getCurrentTps(this.webServerUrl, period);
276
+ }
277
+ /**
278
+ * Get peak transactions per second
279
+ *
280
+ * @param period - Time period for the query
281
+ * @returns Peak TPS value
282
+ * @throws {NordError} If the request fails
283
+ */
284
+ async getPeakTps(period = "24h") {
285
+ return metrics.getPeakTps(this.webServerUrl, period);
286
+ }
287
+ /**
288
+ * Get median transaction latency
289
+ *
290
+ * @param period - Time period for the query
291
+ * @returns Median latency in milliseconds
292
+ * @throws {NordError} If the request fails
293
+ */
294
+ async getMedianLatency(period = "1m") {
295
+ return metrics.getMedianLatency(this.webServerUrl, period);
296
+ }
297
+ /**
298
+ * Get total transaction count
299
+ *
300
+ * @returns Total transaction count
301
+ * @throws {NordError} If the request fails
302
+ */
303
+ async getTotalTransactions() {
304
+ return metrics.getTotalTransactions(this.webServerUrl);
305
+ }
306
+ /**
307
+ * Query Prometheus metrics
308
+ *
309
+ * @param params - Prometheus query parameters
310
+ * @returns Query result as a number
311
+ * @throws {NordError} If the request fails
312
+ */
313
+ async queryPrometheus(params) {
314
+ return metrics.queryPrometheus(this.webServerUrl, params);
315
+ }
316
+ /**
317
+ * Subscribe to orderbook updates for a market
318
+ *
319
+ * @param symbol - Market symbol
320
+ * @returns Orderbook subscription
321
+ * @throws {NordError} If symbol is invalid
322
+ */
323
+ subscribeOrderbook(symbol) {
324
+ if (!symbol || typeof symbol !== "string") {
325
+ throw new NordError_1.NordError("Invalid market symbol");
326
+ }
327
+ const subscription = new events_1.EventEmitter();
328
+ const wsClient = this.createWebSocketClient({
329
+ deltas: [symbol],
330
+ });
331
+ const handleDelta = (update) => {
332
+ if (update.symbol !== symbol) {
333
+ return;
334
+ }
335
+ subscription.emit("message", update);
336
+ };
337
+ wsClient.on("delta", handleDelta);
338
+ subscription.close = () => {
339
+ wsClient.unsubscribe([`deltas@${symbol}`]);
340
+ wsClient.removeListener("delta", handleDelta);
341
+ subscription.removeAllListeners();
342
+ };
343
+ return subscription;
344
+ }
345
+ /**
346
+ * Subscribe to trade updates for a market
347
+ *
348
+ * @param symbol - Market symbol
349
+ * @returns Trade subscription
350
+ * @throws {NordError} If symbol is invalid
351
+ */
352
+ subscribeTrades(symbol) {
353
+ if (!symbol || typeof symbol !== "string") {
354
+ throw new NordError_1.NordError("Invalid market symbol");
355
+ }
356
+ const subscription = new events_1.EventEmitter();
357
+ const wsClient = this.createWebSocketClient({
358
+ trades: [symbol],
359
+ });
360
+ const handleTrade = (update) => {
361
+ if (update.symbol !== symbol) {
362
+ return;
363
+ }
364
+ subscription.emit("message", update);
365
+ };
366
+ wsClient.on("trades", handleTrade);
367
+ subscription.close = () => {
368
+ wsClient.unsubscribe([`trades@${symbol}`]);
369
+ wsClient.removeListener("trades", handleTrade);
370
+ subscription.removeAllListeners();
371
+ };
372
+ return subscription;
373
+ }
374
+ /**
375
+ * Subscribe to account updates
376
+ *
377
+ * @param accountId - Account ID to subscribe to
378
+ * @returns User subscription
379
+ * @throws {NordError} If accountId is invalid
380
+ */
381
+ subscribeAccount(accountId) {
382
+ if (isNaN(accountId) || accountId <= 0) {
383
+ throw new NordError_1.NordError("Invalid account ID");
384
+ }
385
+ const subscription = new events_1.EventEmitter();
386
+ const wsClient = this.createWebSocketClient({
387
+ accounts: [accountId],
388
+ });
389
+ const handleAccountUpdate = (update) => {
390
+ if (update.account_id !== accountId) {
391
+ return;
392
+ }
393
+ subscription.emit("message", update);
394
+ };
395
+ wsClient.on("account", handleAccountUpdate);
396
+ subscription.close = () => {
397
+ wsClient.unsubscribe([`account@${accountId}`]);
398
+ wsClient.removeListener("account", handleAccountUpdate);
399
+ subscription.removeAllListeners();
400
+ };
401
+ return subscription;
402
+ }
403
+ /**
404
+ * Get trades for a market
405
+ *
406
+ * @param query - Trades query parameters
407
+ * @returns Trades response
408
+ * @throws {NordError} If the request fails
409
+ */
410
+ async getTrades(query) {
411
+ if (query.sinceRcf3339 && !utils.isRfc3339(query.sinceRcf3339)) {
412
+ throw new NordError_1.NordError(`Invalid RFC3339 timestamp: ${query.sinceRcf3339}`);
413
+ }
414
+ if (query.untilRfc3339 && !utils.isRfc3339(query.untilRfc3339)) {
415
+ throw new NordError_1.NordError(`Invalid RFC3339 timestamp: ${query.untilRfc3339}`);
416
+ }
417
+ return await this.GET("/trades", {
418
+ params: {
419
+ query: {
420
+ takerId: query.takerId,
421
+ makerId: query.makerId,
422
+ marketId: query.marketId,
423
+ pageSize: query.pageSize,
424
+ takerSide: query.takerSide,
425
+ since: query.sinceRcf3339,
426
+ until: query.untilRfc3339,
427
+ startInclusive: query.pageId,
428
+ },
429
+ },
430
+ });
431
+ }
432
+ /**
433
+ * Get user account IDs
434
+ *
435
+ * @param query - User account IDs query parameters
436
+ * @returns User account IDs response
437
+ * @throws {NordError} If the request fails
438
+ */
439
+ async getUser(query) {
440
+ const r = await this.httpClient.GET("/user/{pubkey}", {
441
+ params: {
442
+ path: { pubkey: query.pubkey.toString() },
443
+ },
444
+ });
445
+ if (r.response.status === 404) {
446
+ return null;
447
+ }
448
+ return r.data;
449
+ }
450
+ /**
451
+ * Get orderbook for a market
452
+ *
453
+ * @param query - Orderbook query parameters (either market_id or symbol must be provided)
454
+ * @returns Orderbook response
455
+ * @throws {NordError} If the request fails or if the market symbol is unknown
456
+ * @remarks It's recommended to initialize the Nord client using the static `initNord` method
457
+ * to ensure market information is properly loaded before calling this method.
458
+ */
459
+ async getOrderbook(query) {
460
+ // If only symbol is provided, convert it to market_id
461
+ let marketId;
462
+ if (query.symbol && query.market_id === undefined) {
463
+ // If the map is empty, try to fetch market information first
464
+ if (this.symbolToMarketId.size === 0) {
465
+ await this.fetchNordInfo();
466
+ }
467
+ const id = this.symbolToMarketId.get(query.symbol);
468
+ if (id === undefined) {
469
+ throw new NordError_1.NordError(`Unknown market symbol: ${query.symbol}`);
470
+ }
471
+ marketId = id;
472
+ }
473
+ else if (query.market_id !== undefined) {
474
+ marketId = query.market_id;
475
+ }
476
+ else {
477
+ throw new NordError_1.NordError("Either symbol or market_id must be provided for orderbook query");
478
+ }
479
+ return await this.GET("/market/{market_id}/orderbook", {
480
+ params: {
481
+ path: { market_id: marketId },
482
+ },
483
+ });
484
+ }
485
+ /**
486
+ * Get information about the Nord server
487
+ *
488
+ * @returns Information about markets and tokens
489
+ * @throws {NordError} If the request fails
490
+ */
491
+ async getInfo() {
492
+ return await this.GET("/info", {});
493
+ }
494
+ /**
495
+ * Get account information
496
+ *
497
+ * @param accountId - Account ID to get information for
498
+ * @returns Account information
499
+ * @throws {NordError} If the request fails
500
+ */
501
+ async getAccount(accountId) {
502
+ return await this.GET("/account/{account_id}", {
503
+ params: {
504
+ path: { account_id: accountId },
505
+ },
506
+ });
507
+ }
508
+ /**
509
+ * Get market statistics (alias for marketsStats for backward compatibility)
510
+ *
511
+ * @returns Market statistics response
512
+ */
513
+ async getMarketStats({ marketId, }) {
514
+ return await this.GET("/market/{market_id}/stats", {
515
+ params: {
516
+ path: { market_id: marketId },
517
+ },
518
+ });
519
+ }
520
+ /**
521
+ * Check if an account exists for the given address
522
+ *
523
+ * @param address - The public key address to check
524
+ * @returns True if the account exists, false otherwise
525
+ * @deprecated use getUser instead
526
+ */
527
+ async accountExists(pubkey) {
528
+ return !!(await this.getUser({ pubkey }));
529
+ }
530
+ }
531
+ exports.Nord = Nord;