@n1xyz/nord-ts 0.1.7 → 0.1.9
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/actions.js +39 -82
- package/dist/bundle.js +79181 -0
- package/dist/client/Nord.d.ts +13 -2
- package/dist/client/Nord.js +68 -78
- package/dist/client/NordAdmin.d.ts +2 -2
- package/dist/client/NordAdmin.js +57 -89
- package/dist/client/NordUser.js +118 -147
- package/dist/const.js +5 -8
- package/dist/error.js +7 -5
- package/dist/gen/nord_pb.js +88 -92
- package/dist/gen/openapi.d.ts +83 -6
- package/dist/gen/openapi.js +1 -2
- package/dist/index.js +10 -49
- package/dist/types.d.ts +3 -0
- package/dist/types.js +21 -60
- package/dist/utils.js +38 -86
- package/dist/websocket/NordWebSocketClient.js +12 -17
- package/dist/websocket/Subscriber.js +6 -7
- package/dist/websocket/events.js +1 -2
- package/dist/websocket/index.js +10 -15
- package/package.json +3 -4
- package/dist/api/client.d.ts +0 -14
- package/dist/api/client.js +0 -45
- package/dist/bridge/client.d.ts +0 -151
- package/dist/bridge/client.js +0 -434
- package/dist/bridge/const.d.ts +0 -23
- package/dist/bridge/const.js +0 -47
- package/dist/bridge/index.d.ts +0 -4
- package/dist/bridge/index.js +0 -23
- package/dist/bridge/types.d.ts +0 -120
- package/dist/bridge/types.js +0 -18
- package/dist/bridge/utils.d.ts +0 -64
- package/dist/bridge/utils.js +0 -131
- package/dist/gen/common.d.ts +0 -68
- package/dist/gen/common.js +0 -215
- package/dist/gen/nord.d.ts +0 -882
- package/dist/gen/nord.js +0 -6520
- package/dist/idl/bridge.d.ts +0 -569
- package/dist/idl/bridge.js +0 -8
- package/dist/idl/bridge.json +0 -1506
- package/dist/idl/index.d.ts +0 -607
- package/dist/idl/index.js +0 -8
- package/dist/nord/api/actions.d.ts +0 -126
- package/dist/nord/api/actions.js +0 -397
- package/dist/nord/api/core.d.ts +0 -16
- package/dist/nord/api/core.js +0 -81
- package/dist/nord/api/market.d.ts +0 -36
- package/dist/nord/api/market.js +0 -96
- package/dist/nord/api/metrics.d.ts +0 -67
- package/dist/nord/api/metrics.js +0 -229
- package/dist/nord/api/queries.d.ts +0 -46
- package/dist/nord/api/queries.js +0 -109
- package/dist/nord/api/triggers.d.ts +0 -7
- package/dist/nord/api/triggers.js +0 -38
- package/dist/nord/client/Nord.d.ts +0 -396
- package/dist/nord/client/Nord.js +0 -747
- package/dist/nord/client/NordAdmin.d.ts +0 -259
- package/dist/nord/client/NordAdmin.js +0 -395
- package/dist/nord/client/NordClient.d.ts +0 -33
- package/dist/nord/client/NordClient.js +0 -45
- package/dist/nord/client/NordUser.d.ts +0 -362
- package/dist/nord/client/NordUser.js +0 -781
- package/dist/nord/index.d.ts +0 -11
- package/dist/nord/index.js +0 -36
- package/dist/nord/models/Subscriber.d.ts +0 -37
- package/dist/nord/models/Subscriber.js +0 -25
- package/dist/nord/utils/NordError.d.ts +0 -35
- package/dist/nord/utils/NordError.js +0 -49
- package/src/actions.ts +0 -333
- package/src/client/Nord.ts +0 -934
- package/src/client/NordAdmin.ts +0 -484
- package/src/client/NordUser.ts +0 -1122
- package/src/const.ts +0 -34
- package/src/error.ts +0 -76
- package/src/gen/.gitkeep +0 -0
- package/src/gen/nord_pb.ts +0 -5053
- package/src/gen/openapi.ts +0 -2904
- package/src/index.ts +0 -11
- package/src/types.ts +0 -327
- package/src/utils.ts +0 -266
- package/src/websocket/NordWebSocketClient.ts +0 -316
- package/src/websocket/Subscriber.ts +0 -56
- package/src/websocket/events.ts +0 -31
- package/src/websocket/index.ts +0 -105
package/src/client/Nord.ts
DELETED
|
@@ -1,934 +0,0 @@
|
|
|
1
|
-
import { ProtonClient } from "@n1xyz/proton";
|
|
2
|
-
import { Connection, PublicKey } from "@solana/web3.js";
|
|
3
|
-
import { EventEmitter } from "events";
|
|
4
|
-
import createClient, { Client, FetchOptions } from "openapi-fetch";
|
|
5
|
-
import * as proto from "../gen/nord_pb";
|
|
6
|
-
import type { paths } from "../gen/openapi.ts";
|
|
7
|
-
import {
|
|
8
|
-
Account,
|
|
9
|
-
AccountPnlPage,
|
|
10
|
-
AccountPnlQuery,
|
|
11
|
-
ActionResponse,
|
|
12
|
-
MarketsInfo,
|
|
13
|
-
Market,
|
|
14
|
-
MarketStats,
|
|
15
|
-
NordConfig,
|
|
16
|
-
OrderbookQuery,
|
|
17
|
-
OrderbookResponse,
|
|
18
|
-
FeeTierConfig,
|
|
19
|
-
SubscriptionPattern,
|
|
20
|
-
Token,
|
|
21
|
-
TradesResponse,
|
|
22
|
-
User,
|
|
23
|
-
AccountTriggerInfo,
|
|
24
|
-
HistoryTriggerQuery,
|
|
25
|
-
TriggerHistoryPage,
|
|
26
|
-
FeeTierId,
|
|
27
|
-
AccountFeeTierPage,
|
|
28
|
-
PageResultStringOrderInfo,
|
|
29
|
-
PageResultStringTrade,
|
|
30
|
-
OrderInfoFromApi,
|
|
31
|
-
TokenStats,
|
|
32
|
-
FillRole,
|
|
33
|
-
} from "../types";
|
|
34
|
-
import * as utils from "../utils";
|
|
35
|
-
import { NordWebSocketClient } from "../websocket/index";
|
|
36
|
-
import { initWebSocketClient } from "../websocket";
|
|
37
|
-
import {
|
|
38
|
-
OrderbookSubscription,
|
|
39
|
-
TradeSubscription,
|
|
40
|
-
} from "../websocket/Subscriber";
|
|
41
|
-
import { NordError } from "../error";
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* User subscription interface
|
|
45
|
-
*/
|
|
46
|
-
export interface UserSubscription extends EventEmitter {
|
|
47
|
-
close: () => void;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Main Nord client class for interacting with the Nord API
|
|
52
|
-
*/
|
|
53
|
-
export class Nord {
|
|
54
|
-
/** Base URL for the Nord web server */
|
|
55
|
-
public readonly webServerUrl: string;
|
|
56
|
-
|
|
57
|
-
/** Solana RPC URL */
|
|
58
|
-
public readonly solanaConnection: Connection;
|
|
59
|
-
|
|
60
|
-
/** Available markets */
|
|
61
|
-
public markets: Market[] = [];
|
|
62
|
-
|
|
63
|
-
/** Available tokens */
|
|
64
|
-
public tokens: Token[] = [];
|
|
65
|
-
|
|
66
|
-
/** Map of symbol to market_id */
|
|
67
|
-
private symbolToMarketId: Map<string, number> = new Map();
|
|
68
|
-
|
|
69
|
-
/** Proton client for proton related operations */
|
|
70
|
-
public protonClient: ProtonClient;
|
|
71
|
-
|
|
72
|
-
/** HTTP client for Nord operations */
|
|
73
|
-
public readonly httpClient: Client<paths>;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Create a new Nord client
|
|
77
|
-
*
|
|
78
|
-
* @param config - Configuration options for the Nord client
|
|
79
|
-
* @param config.webServerUrl - Base URL for the Nord web server
|
|
80
|
-
* @param config.solanaUrl - Solana cluster URL
|
|
81
|
-
* @throws {Error} If required configuration is missing
|
|
82
|
-
*/
|
|
83
|
-
private constructor({
|
|
84
|
-
solanaConnection,
|
|
85
|
-
webServerUrl,
|
|
86
|
-
protonClient,
|
|
87
|
-
}: Readonly<{
|
|
88
|
-
solanaConnection: Connection;
|
|
89
|
-
webServerUrl: string;
|
|
90
|
-
protonClient: ProtonClient;
|
|
91
|
-
}>) {
|
|
92
|
-
this.webServerUrl = webServerUrl;
|
|
93
|
-
this.solanaConnection = solanaConnection;
|
|
94
|
-
this.protonClient = protonClient;
|
|
95
|
-
this.httpClient = createClient<paths>({ baseUrl: webServerUrl });
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Create a WebSocket client with specific subscriptions
|
|
100
|
-
*
|
|
101
|
-
* @param trades - Market symbols to subscribe to for trade updates
|
|
102
|
-
* @param deltas - Market symbols to subscribe to for orderbook delta updates
|
|
103
|
-
* @param accounts - Account IDs to subscribe to for account updates
|
|
104
|
-
* @returns A new WebSocket client with the requested subscriptions
|
|
105
|
-
* @throws {NordError} If invalid subscription options are provided
|
|
106
|
-
*
|
|
107
|
-
* @example
|
|
108
|
-
* // Create a client for trades and deltas from one market and an account
|
|
109
|
-
* const wsClient = nord.createWebSocketClient({
|
|
110
|
-
* trades: ["BTCUSDC"],
|
|
111
|
-
* deltas: ["BTCUSDC"],
|
|
112
|
-
* accounts: [123]
|
|
113
|
-
* });
|
|
114
|
-
*
|
|
115
|
-
* @example
|
|
116
|
-
* // Create a client for trades from multiple markets
|
|
117
|
-
* const tradesClient = nord.createWebSocketClient({
|
|
118
|
-
* trades: ["BTCUSDC", "ETHUSDC"]
|
|
119
|
-
* });
|
|
120
|
-
*/
|
|
121
|
-
public createWebSocketClient({
|
|
122
|
-
trades,
|
|
123
|
-
deltas,
|
|
124
|
-
accounts,
|
|
125
|
-
}: Readonly<{
|
|
126
|
-
trades?: string[];
|
|
127
|
-
deltas?: string[];
|
|
128
|
-
accounts?: number[];
|
|
129
|
-
}>): NordWebSocketClient {
|
|
130
|
-
const subscriptions: SubscriptionPattern[] = [];
|
|
131
|
-
|
|
132
|
-
// Add trade subscriptions
|
|
133
|
-
if (trades && trades.length > 0) {
|
|
134
|
-
trades.forEach((symbol) => {
|
|
135
|
-
subscriptions.push(`trades@${symbol}` as SubscriptionPattern);
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Add delta subscriptions
|
|
140
|
-
if (deltas && deltas.length > 0) {
|
|
141
|
-
deltas.forEach((symbol) => {
|
|
142
|
-
subscriptions.push(`deltas@${symbol}` as SubscriptionPattern);
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Add account subscriptions
|
|
147
|
-
if (accounts && accounts.length > 0) {
|
|
148
|
-
accounts.forEach((accountId) => {
|
|
149
|
-
if (isNaN(accountId) || accountId <= 0) {
|
|
150
|
-
throw new NordError(
|
|
151
|
-
`Invalid account ID: ${accountId}. Must be a positive number.`,
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
subscriptions.push(`account@${accountId}` as SubscriptionPattern);
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Validate that at least one subscription was provided
|
|
159
|
-
if (subscriptions.length === 0) {
|
|
160
|
-
throw new NordError("At least one subscription must be provided");
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Create and return a new WebSocket client
|
|
164
|
-
return initWebSocketClient(this.webServerUrl, subscriptions);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
private async GET<P extends keyof paths & string>(
|
|
168
|
-
path: P,
|
|
169
|
-
options: FetchOptions<paths[P]["get"]>,
|
|
170
|
-
) {
|
|
171
|
-
const r = await this.httpClient.GET(path, options);
|
|
172
|
-
if (r.error) {
|
|
173
|
-
throw new NordError(`failed to GET ${path}`, { cause: r.error });
|
|
174
|
-
}
|
|
175
|
-
if (r.data === undefined) {
|
|
176
|
-
// this should never happen, but the type checker seems unhappy.
|
|
177
|
-
// if we catch this we'll need to debug accordingly.
|
|
178
|
-
throw new NordError("internal assertion violation", { cause: r });
|
|
179
|
-
}
|
|
180
|
-
return r.data;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Get the current timestamp from the Nord server
|
|
185
|
-
*
|
|
186
|
-
* @returns Current timestamp as a bigint
|
|
187
|
-
* @throws {NordError} If the request fails
|
|
188
|
-
*/
|
|
189
|
-
async getTimestamp(): Promise<bigint> {
|
|
190
|
-
return BigInt(await this.GET("/timestamp", {}));
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Get the last event nonce from the Nord server
|
|
195
|
-
*
|
|
196
|
-
* @returns Next action nonce
|
|
197
|
-
* @throws {NordError} If the request fails
|
|
198
|
-
*/
|
|
199
|
-
async getActionNonce(): Promise<number> {
|
|
200
|
-
return await this.GET("/event/last-acked-nonce", {});
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Get the admin list from the Nord server
|
|
205
|
-
*
|
|
206
|
-
* @returns List of admin registration keys paired with their ACL role mask
|
|
207
|
-
* @throws {NordError} If the request fails
|
|
208
|
-
*/
|
|
209
|
-
async getAdminList(): Promise<Array<[string, number]>> {
|
|
210
|
-
return await this.GET("/admin", {});
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Fetch information about Nord markets and tokens
|
|
215
|
-
*
|
|
216
|
-
* @throws {NordError} If the request fails
|
|
217
|
-
*/
|
|
218
|
-
async fetchNordInfo(): Promise<void> {
|
|
219
|
-
try {
|
|
220
|
-
const info = await this.GET("/info", {});
|
|
221
|
-
this.markets = info.markets;
|
|
222
|
-
this.tokens = info.tokens;
|
|
223
|
-
|
|
224
|
-
// Populate the symbolToMarketId map
|
|
225
|
-
this.symbolToMarketId.clear();
|
|
226
|
-
info.markets.forEach((market) => {
|
|
227
|
-
this.symbolToMarketId.set(market.symbol, market.marketId);
|
|
228
|
-
});
|
|
229
|
-
} catch (error) {
|
|
230
|
-
throw new NordError("Failed to fetch Nord info", { cause: error });
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/** @deprecated use Nord.new */
|
|
235
|
-
public static async initNord(x: Readonly<NordConfig>): Promise<Nord> {
|
|
236
|
-
return await Nord.new(x);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Initialize a new Nord client
|
|
241
|
-
*
|
|
242
|
-
* @param nordConfig - Configuration options for the Nord client
|
|
243
|
-
* @param nordConfig.webServerUrl - Base URL for the Nord web server
|
|
244
|
-
* @param nordConfig.app - App address
|
|
245
|
-
* @param nordConfig.solanaUrl - Solana cluster URL
|
|
246
|
-
* @returns Initialized Nord client
|
|
247
|
-
* @throws {NordError} If initialization fails
|
|
248
|
-
*/
|
|
249
|
-
public static async new({
|
|
250
|
-
app,
|
|
251
|
-
solanaConnection,
|
|
252
|
-
webServerUrl,
|
|
253
|
-
protonUrl,
|
|
254
|
-
}: Readonly<NordConfig>): Promise<Nord> {
|
|
255
|
-
const protonClient = await ProtonClient.init({
|
|
256
|
-
protonUrl: protonUrl ?? webServerUrl,
|
|
257
|
-
app: new PublicKey(app),
|
|
258
|
-
solConn: solanaConnection,
|
|
259
|
-
});
|
|
260
|
-
const nord = new Nord({
|
|
261
|
-
protonClient,
|
|
262
|
-
solanaConnection,
|
|
263
|
-
webServerUrl,
|
|
264
|
-
});
|
|
265
|
-
await nord.init();
|
|
266
|
-
return nord;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Initialize the Nord client
|
|
271
|
-
* @private
|
|
272
|
-
*/
|
|
273
|
-
private async init(): Promise<void> {
|
|
274
|
-
await this.fetchNordInfo();
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Query a specific action
|
|
279
|
-
*
|
|
280
|
-
* @param actionId - Action identifier to fetch
|
|
281
|
-
* @returns Action response
|
|
282
|
-
* @throws {NordError} If the request fails
|
|
283
|
-
*/
|
|
284
|
-
async queryAction({
|
|
285
|
-
actionId,
|
|
286
|
-
}: Readonly<{
|
|
287
|
-
actionId: number;
|
|
288
|
-
}>): Promise<ActionResponse | null> {
|
|
289
|
-
return (
|
|
290
|
-
(
|
|
291
|
-
await this.queryRecentActions({
|
|
292
|
-
from: actionId,
|
|
293
|
-
to: actionId,
|
|
294
|
-
})
|
|
295
|
-
)[0] ?? null
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Query recent actions
|
|
301
|
-
*
|
|
302
|
-
* @param from - Starting action index (inclusive)
|
|
303
|
-
* @param to - Ending action index (inclusive)
|
|
304
|
-
* @returns Actions response
|
|
305
|
-
* @throws {NordError} If the request fails
|
|
306
|
-
*/
|
|
307
|
-
async queryRecentActions({
|
|
308
|
-
from,
|
|
309
|
-
to,
|
|
310
|
-
}: Readonly<{
|
|
311
|
-
from: number;
|
|
312
|
-
to: number;
|
|
313
|
-
}>): Promise<ActionResponse[]> {
|
|
314
|
-
const xs = await this.GET("/action", {
|
|
315
|
-
params: {
|
|
316
|
-
query: { from, to },
|
|
317
|
-
},
|
|
318
|
-
});
|
|
319
|
-
return xs.map((x) => ({
|
|
320
|
-
actionId: x.actionId,
|
|
321
|
-
action: utils.decodeLengthDelimited(
|
|
322
|
-
Buffer.from(x.payload, "base64"),
|
|
323
|
-
proto.ActionSchema,
|
|
324
|
-
),
|
|
325
|
-
physicalExecTime: new Date(x.physicalTime),
|
|
326
|
-
}));
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Get the last action ID
|
|
331
|
-
*
|
|
332
|
-
* @returns Last action ID
|
|
333
|
-
* @throws {NordError} If the request fails
|
|
334
|
-
*/
|
|
335
|
-
async getLastActionId(): Promise<number> {
|
|
336
|
-
return await this.GET("/action/last-executed-id", {});
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Subscribe to orderbook updates for a market
|
|
341
|
-
*
|
|
342
|
-
* @param symbol - Market symbol
|
|
343
|
-
* @returns Orderbook subscription
|
|
344
|
-
* @throws {NordError} If symbol is invalid
|
|
345
|
-
*/
|
|
346
|
-
public subscribeOrderbook(symbol: string): OrderbookSubscription {
|
|
347
|
-
if (!symbol || typeof symbol !== "string") {
|
|
348
|
-
throw new NordError("Invalid market symbol");
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
const subscription = new EventEmitter() as OrderbookSubscription;
|
|
352
|
-
const wsClient = this.createWebSocketClient({
|
|
353
|
-
deltas: [symbol],
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
const handleDelta = (update: {
|
|
357
|
-
symbol: string;
|
|
358
|
-
bids: [number, number][];
|
|
359
|
-
asks: [number, number][];
|
|
360
|
-
}) => {
|
|
361
|
-
if (update.symbol !== symbol) {
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
subscription.emit("message", update);
|
|
366
|
-
};
|
|
367
|
-
|
|
368
|
-
wsClient.on("delta", handleDelta);
|
|
369
|
-
|
|
370
|
-
subscription.close = () => {
|
|
371
|
-
wsClient.removeListener("delta", handleDelta);
|
|
372
|
-
subscription.removeAllListeners();
|
|
373
|
-
};
|
|
374
|
-
|
|
375
|
-
return subscription;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Subscribe to trade updates for a market
|
|
380
|
-
*
|
|
381
|
-
* @param symbol - Market symbol
|
|
382
|
-
* @returns Trade subscription
|
|
383
|
-
* @throws {NordError} If symbol is invalid
|
|
384
|
-
*/
|
|
385
|
-
public subscribeTrades(symbol: string): TradeSubscription {
|
|
386
|
-
if (!symbol || typeof symbol !== "string") {
|
|
387
|
-
throw new NordError("Invalid market symbol");
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
const subscription = new EventEmitter() as TradeSubscription;
|
|
391
|
-
const wsClient = this.createWebSocketClient({
|
|
392
|
-
trades: [symbol],
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
const handleTrade = (update: {
|
|
396
|
-
symbol: string;
|
|
397
|
-
trades: Array<{
|
|
398
|
-
price: number;
|
|
399
|
-
size: number;
|
|
400
|
-
side: string;
|
|
401
|
-
timestamp: number;
|
|
402
|
-
}>;
|
|
403
|
-
}) => {
|
|
404
|
-
if (update.symbol !== symbol) {
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
subscription.emit("message", update);
|
|
409
|
-
};
|
|
410
|
-
|
|
411
|
-
wsClient.on("trades", handleTrade);
|
|
412
|
-
|
|
413
|
-
subscription.close = () => {
|
|
414
|
-
wsClient.removeListener("trades", handleTrade);
|
|
415
|
-
subscription.removeAllListeners();
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
return subscription;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* Subscribe to account updates
|
|
423
|
-
*
|
|
424
|
-
* @param accountId - Account ID to subscribe to
|
|
425
|
-
* @returns User subscription
|
|
426
|
-
* @throws {NordError} If accountId is invalid
|
|
427
|
-
*/
|
|
428
|
-
public subscribeAccount(accountId: number): UserSubscription {
|
|
429
|
-
if (isNaN(accountId) || accountId <= 0) {
|
|
430
|
-
throw new NordError("Invalid account ID");
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
const subscription = new EventEmitter() as UserSubscription;
|
|
434
|
-
const wsClient = this.createWebSocketClient({
|
|
435
|
-
accounts: [accountId],
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
const handleAccountUpdate = (update: any) => {
|
|
439
|
-
if (update.account_id !== accountId) {
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
subscription.emit("message", update);
|
|
444
|
-
};
|
|
445
|
-
|
|
446
|
-
wsClient.on("account", handleAccountUpdate);
|
|
447
|
-
|
|
448
|
-
subscription.close = () => {
|
|
449
|
-
wsClient.removeListener("account", handleAccountUpdate);
|
|
450
|
-
subscription.removeAllListeners();
|
|
451
|
-
};
|
|
452
|
-
|
|
453
|
-
return subscription;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
/**
|
|
457
|
-
* Get trades for a market
|
|
458
|
-
*
|
|
459
|
-
* @param marketId - Market identifier to filter by
|
|
460
|
-
* @param takerId - Taker account identifier
|
|
461
|
-
* @param makerId - Maker account identifier
|
|
462
|
-
* @param takerSide - Side executed by the taker
|
|
463
|
-
* @param pageSize - Maximum number of trades to return
|
|
464
|
-
* @param since - RFC3339 timestamp to start from (inclusive)
|
|
465
|
-
* @param until - RFC3339 timestamp to end at (exclusive)
|
|
466
|
-
* @param pageId - Pagination cursor returned from a prior call
|
|
467
|
-
* @returns Trades response
|
|
468
|
-
* @throws {NordError} If the request fails
|
|
469
|
-
*/
|
|
470
|
-
public async getTrades({
|
|
471
|
-
marketId,
|
|
472
|
-
takerId,
|
|
473
|
-
makerId,
|
|
474
|
-
takerSide,
|
|
475
|
-
pageSize,
|
|
476
|
-
since,
|
|
477
|
-
until,
|
|
478
|
-
startInclusive,
|
|
479
|
-
}: Readonly<{
|
|
480
|
-
marketId?: number;
|
|
481
|
-
takerId?: number;
|
|
482
|
-
makerId?: number;
|
|
483
|
-
takerSide?: "bid" | "ask";
|
|
484
|
-
pageSize?: number;
|
|
485
|
-
since?: string;
|
|
486
|
-
until?: string;
|
|
487
|
-
startInclusive?: string;
|
|
488
|
-
}>): Promise<TradesResponse> {
|
|
489
|
-
if (since && !utils.isRfc3339(since)) {
|
|
490
|
-
throw new NordError(`Invalid RFC3339 timestamp: ${since}`);
|
|
491
|
-
}
|
|
492
|
-
if (until && !utils.isRfc3339(until)) {
|
|
493
|
-
throw new NordError(`Invalid RFC3339 timestamp: ${until}`);
|
|
494
|
-
}
|
|
495
|
-
return await this.GET("/trades", {
|
|
496
|
-
params: {
|
|
497
|
-
query: {
|
|
498
|
-
takerId,
|
|
499
|
-
makerId,
|
|
500
|
-
marketId,
|
|
501
|
-
pageSize,
|
|
502
|
-
takerSide,
|
|
503
|
-
since,
|
|
504
|
-
until,
|
|
505
|
-
startInclusive,
|
|
506
|
-
},
|
|
507
|
-
},
|
|
508
|
-
});
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
/**
|
|
512
|
-
* Get user account IDs
|
|
513
|
-
*
|
|
514
|
-
* @param pubkey - User public key to query
|
|
515
|
-
* @returns User account IDs response
|
|
516
|
-
* @throws {NordError} If the request fails
|
|
517
|
-
*/
|
|
518
|
-
public async getUser({
|
|
519
|
-
pubkey,
|
|
520
|
-
}: Readonly<{
|
|
521
|
-
pubkey: string | PublicKey;
|
|
522
|
-
}>): Promise<User | null> {
|
|
523
|
-
const r = await this.httpClient.GET("/user/{pubkey}", {
|
|
524
|
-
params: {
|
|
525
|
-
path: { pubkey: pubkey.toString() },
|
|
526
|
-
},
|
|
527
|
-
});
|
|
528
|
-
if (r.response.status === 404) {
|
|
529
|
-
return null;
|
|
530
|
-
}
|
|
531
|
-
return r.data!;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
/**
|
|
535
|
-
* Get orderbook for a market
|
|
536
|
-
*
|
|
537
|
-
* @param symbol - Market symbol to resolve into an id
|
|
538
|
-
* @param marketId - Market identifier
|
|
539
|
-
* @returns Orderbook response
|
|
540
|
-
* @throws {NordError} If the request fails or if the market symbol is unknown
|
|
541
|
-
* @remarks It's recommended to initialize the Nord client using the static `initNord` method
|
|
542
|
-
* to ensure market information is properly loaded before calling this method.
|
|
543
|
-
*/
|
|
544
|
-
public async getOrderbook({
|
|
545
|
-
symbol,
|
|
546
|
-
marketId,
|
|
547
|
-
}: OrderbookQuery): Promise<OrderbookResponse> {
|
|
548
|
-
// If only symbol is provided, convert it to market_id
|
|
549
|
-
let _marketId: number;
|
|
550
|
-
if (symbol && marketId === undefined) {
|
|
551
|
-
// If the map is empty, try to fetch market information first
|
|
552
|
-
if (this.symbolToMarketId.size === 0) {
|
|
553
|
-
await this.fetchNordInfo();
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
const id = this.symbolToMarketId.get(symbol);
|
|
557
|
-
if (id === undefined) {
|
|
558
|
-
throw new NordError(`Unknown market symbol: ${symbol}`);
|
|
559
|
-
}
|
|
560
|
-
_marketId = id;
|
|
561
|
-
} else if (marketId !== undefined) {
|
|
562
|
-
_marketId = marketId;
|
|
563
|
-
} else {
|
|
564
|
-
throw new NordError(
|
|
565
|
-
"Either symbol or market_id must be provided for orderbook query",
|
|
566
|
-
);
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
return await this.GET("/market/{market_id}/orderbook", {
|
|
570
|
-
params: {
|
|
571
|
-
path: { market_id: _marketId },
|
|
572
|
-
},
|
|
573
|
-
});
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
/**
|
|
577
|
-
* Get information about the Nord server
|
|
578
|
-
*
|
|
579
|
-
* @returns Information about markets and tokens
|
|
580
|
-
* @throws {NordError} If the request fails
|
|
581
|
-
*/
|
|
582
|
-
public async getInfo(): Promise<MarketsInfo> {
|
|
583
|
-
return await this.GET("/info", {});
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
/**
|
|
587
|
-
* Fetch the current fee tier brackets configured on Nord.
|
|
588
|
-
*
|
|
589
|
-
* @returns Array of fee tier identifiers paired with their configuration
|
|
590
|
-
* @throws {NordError} If the request fails
|
|
591
|
-
*/
|
|
592
|
-
public async getFeeBrackets(): Promise<Array<[FeeTierId, FeeTierConfig]>> {
|
|
593
|
-
return await this.GET("/fee/brackets/info", {});
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
/**
|
|
597
|
-
* Retrieve the fee tier assigned to a specific account.
|
|
598
|
-
*
|
|
599
|
-
* @param accountId - Account identifier to query
|
|
600
|
-
* @returns Fee tier details for the requested account
|
|
601
|
-
* @throws {NordError} If the request fails
|
|
602
|
-
*/
|
|
603
|
-
public async getAccountFeeTier(accountId: number): Promise<FeeTierId> {
|
|
604
|
-
return await this.GET("/account/{account_id}/fee/tier", {
|
|
605
|
-
params: {
|
|
606
|
-
path: { account_id: accountId },
|
|
607
|
-
},
|
|
608
|
-
});
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
/**
|
|
612
|
-
* Get account information
|
|
613
|
-
*
|
|
614
|
-
* @param accountId - Account ID to get information for
|
|
615
|
-
* @returns Account information
|
|
616
|
-
* @throws {NordError} If the request fails
|
|
617
|
-
*/
|
|
618
|
-
public async getAccount(accountId: number): Promise<Account> {
|
|
619
|
-
return await this.GET("/account/{account_id}", {
|
|
620
|
-
params: {
|
|
621
|
-
path: { account_id: accountId },
|
|
622
|
-
},
|
|
623
|
-
});
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
/**
|
|
627
|
-
* Get the public key associated with an account id.
|
|
628
|
-
*
|
|
629
|
-
* @param accountId - Account id to query
|
|
630
|
-
* @returns Base58-encoded account public key
|
|
631
|
-
* @throws {NordError} If the request fails
|
|
632
|
-
*/
|
|
633
|
-
public async getAccountPubkey(accountId: number): Promise<string> {
|
|
634
|
-
return await this.GET("/account/{account_id}/pubkey", {
|
|
635
|
-
params: {
|
|
636
|
-
path: { account_id: accountId },
|
|
637
|
-
},
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
/**
|
|
642
|
-
* Get the withdrawal fee charged for an account.
|
|
643
|
-
*
|
|
644
|
-
* @param accountId - Account id to query
|
|
645
|
-
* @returns Withdrawal fee quoted in quote token units
|
|
646
|
-
* @throws {NordError} If the request fails
|
|
647
|
-
*/
|
|
648
|
-
public async getAccountWithdrawalFee(accountId: number): Promise<number> {
|
|
649
|
-
return await this.GET("/account/{account_id}/fees/withdrawal", {
|
|
650
|
-
params: {
|
|
651
|
-
path: { account_id: accountId },
|
|
652
|
-
},
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
/**
|
|
657
|
-
* Get open orders for an account.
|
|
658
|
-
*
|
|
659
|
-
* @param accountId - Account id to query
|
|
660
|
-
* @param startInclusive - Pagination cursor (client order id) to resume from
|
|
661
|
-
* @param pageSize - Maximum number of orders to return
|
|
662
|
-
* @returns Page of orders keyed by client order id
|
|
663
|
-
* @throws {NordError} If the request fails
|
|
664
|
-
*/
|
|
665
|
-
public async getAccountOrders(
|
|
666
|
-
accountId: number,
|
|
667
|
-
{
|
|
668
|
-
startInclusive,
|
|
669
|
-
pageSize,
|
|
670
|
-
}: Readonly<{
|
|
671
|
-
startInclusive?: string | null;
|
|
672
|
-
pageSize?: number | null;
|
|
673
|
-
}> = {},
|
|
674
|
-
): Promise<PageResultStringOrderInfo> {
|
|
675
|
-
return await this.GET("/account/{account_id}/orders", {
|
|
676
|
-
params: {
|
|
677
|
-
path: { account_id: accountId },
|
|
678
|
-
query: {
|
|
679
|
-
startInclusive,
|
|
680
|
-
pageSize,
|
|
681
|
-
},
|
|
682
|
-
},
|
|
683
|
-
});
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
/**
|
|
687
|
-
* List account fee tiers with pagination support.
|
|
688
|
-
*
|
|
689
|
-
* @param startInclusive - Account id cursor to resume from
|
|
690
|
-
* @param pageSize - Maximum number of entries to return
|
|
691
|
-
*/
|
|
692
|
-
public async getAccountsFeeTiers({
|
|
693
|
-
startInclusive,
|
|
694
|
-
pageSize,
|
|
695
|
-
}: Readonly<{
|
|
696
|
-
startInclusive?: number | null;
|
|
697
|
-
pageSize?: number | null;
|
|
698
|
-
}> = {}): Promise<AccountFeeTierPage> {
|
|
699
|
-
return await this.GET("/accounts/fee-tiers", {
|
|
700
|
-
params: {
|
|
701
|
-
query: {
|
|
702
|
-
startInclusive: startInclusive ?? undefined,
|
|
703
|
-
pageSize: pageSize ?? undefined,
|
|
704
|
-
},
|
|
705
|
-
},
|
|
706
|
-
});
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
/**
|
|
710
|
-
* Get profit and loss history for an account
|
|
711
|
-
*
|
|
712
|
-
* @param accountId - Account ID to query
|
|
713
|
-
* @param since - RFC3339 timestamp to start from (inclusive)
|
|
714
|
-
* @param until - RFC3339 timestamp to end at (exclusive)
|
|
715
|
-
* @param startInclusive - Pagination cursor to resume from
|
|
716
|
-
* @param pageSize - Maximum number of entries to return
|
|
717
|
-
* @returns Page of PnL entries ordered from latest to oldest
|
|
718
|
-
* @throws {NordError} If the request fails
|
|
719
|
-
*/
|
|
720
|
-
public async getAccountPnl(
|
|
721
|
-
accountId: number,
|
|
722
|
-
{
|
|
723
|
-
since,
|
|
724
|
-
until,
|
|
725
|
-
startInclusive,
|
|
726
|
-
pageSize,
|
|
727
|
-
}: Readonly<Partial<AccountPnlQuery>> = {},
|
|
728
|
-
): Promise<AccountPnlPage> {
|
|
729
|
-
return await this.GET("/account/{account_id}/pnl", {
|
|
730
|
-
params: {
|
|
731
|
-
path: { account_id: accountId },
|
|
732
|
-
query: {
|
|
733
|
-
since,
|
|
734
|
-
until,
|
|
735
|
-
startInclusive,
|
|
736
|
-
pageSize,
|
|
737
|
-
},
|
|
738
|
-
},
|
|
739
|
-
});
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
/**
|
|
743
|
-
* Get market statistics (alias for marketsStats for backward compatibility)
|
|
744
|
-
*
|
|
745
|
-
*
|
|
746
|
-
* @param marketId - Market identifier
|
|
747
|
-
*
|
|
748
|
-
* @returns Market statistics response
|
|
749
|
-
*/
|
|
750
|
-
public async getMarketStats({
|
|
751
|
-
marketId,
|
|
752
|
-
}: Readonly<{
|
|
753
|
-
marketId: number;
|
|
754
|
-
}>): Promise<MarketStats> {
|
|
755
|
-
return await this.GET("/market/{market_id}/stats", {
|
|
756
|
-
params: {
|
|
757
|
-
path: { market_id: marketId },
|
|
758
|
-
},
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
/**
|
|
763
|
-
* Fetch the per-market fee quote for an account.
|
|
764
|
-
*
|
|
765
|
-
* @param marketId - Market identifier
|
|
766
|
-
* @param feeKind - Fill role (maker/taker) to quote
|
|
767
|
-
* @param accountId - Account identifier to quote
|
|
768
|
-
* @returns Fee in quote token units (negative means fee is charged)
|
|
769
|
-
* @throws {NordError} If the request fails
|
|
770
|
-
*/
|
|
771
|
-
public async getMarketFee({
|
|
772
|
-
marketId,
|
|
773
|
-
feeKind,
|
|
774
|
-
accountId,
|
|
775
|
-
}: Readonly<{
|
|
776
|
-
marketId: number;
|
|
777
|
-
feeKind: FillRole;
|
|
778
|
-
accountId: number;
|
|
779
|
-
}>): Promise<number> {
|
|
780
|
-
return await this.GET("/market/{market_id}/fees/{fee_kind}/{account_id}", {
|
|
781
|
-
params: {
|
|
782
|
-
path: {
|
|
783
|
-
market_id: marketId,
|
|
784
|
-
fee_kind: feeKind,
|
|
785
|
-
account_id: accountId,
|
|
786
|
-
},
|
|
787
|
-
},
|
|
788
|
-
});
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
/**
|
|
792
|
-
* Fetch token statistics such as index price and oracle metadata.
|
|
793
|
-
*
|
|
794
|
-
* @param tokenId - Token identifier
|
|
795
|
-
* @returns Token stats
|
|
796
|
-
* @throws {NordError} If the request fails
|
|
797
|
-
*/
|
|
798
|
-
public async getTokenStats(tokenId: number): Promise<TokenStats> {
|
|
799
|
-
return await this.GET("/tokens/{token_id}/stats", {
|
|
800
|
-
params: {
|
|
801
|
-
path: { token_id: tokenId },
|
|
802
|
-
},
|
|
803
|
-
});
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
/**
|
|
807
|
-
* Get order summary by order id.
|
|
808
|
-
*
|
|
809
|
-
* @param orderId - Order identifier
|
|
810
|
-
* @returns Order information
|
|
811
|
-
* @throws {NordError} If the request fails
|
|
812
|
-
*/
|
|
813
|
-
public async getOrder(orderId: string): Promise<OrderInfoFromApi> {
|
|
814
|
-
return await this.GET("/order/{order_id}", {
|
|
815
|
-
params: {
|
|
816
|
-
path: { order_id: orderId },
|
|
817
|
-
},
|
|
818
|
-
});
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
/**
|
|
822
|
-
* Get trade history for a specific order.
|
|
823
|
-
*
|
|
824
|
-
* @param orderId - Order identifier
|
|
825
|
-
* @param startInclusive - Trade pagination cursor
|
|
826
|
-
* @param pageSize - Maximum number of trades to return
|
|
827
|
-
* @returns Page of trades associated with the order
|
|
828
|
-
* @throws {NordError} If the request fails
|
|
829
|
-
*/
|
|
830
|
-
public async getOrderTrades(
|
|
831
|
-
orderId: string,
|
|
832
|
-
{
|
|
833
|
-
startInclusive,
|
|
834
|
-
pageSize,
|
|
835
|
-
}: Readonly<{
|
|
836
|
-
startInclusive?: string | null;
|
|
837
|
-
pageSize?: number | null;
|
|
838
|
-
}> = {},
|
|
839
|
-
): Promise<PageResultStringTrade> {
|
|
840
|
-
return await this.GET("/order/{order_id}/trades", {
|
|
841
|
-
params: {
|
|
842
|
-
path: { order_id: orderId },
|
|
843
|
-
query: {
|
|
844
|
-
startInclusive,
|
|
845
|
-
pageSize,
|
|
846
|
-
},
|
|
847
|
-
},
|
|
848
|
-
});
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
/**
|
|
852
|
-
* Check if an account exists for the given address
|
|
853
|
-
*
|
|
854
|
-
* @param address - The public key address to check
|
|
855
|
-
* @returns True if the account exists, false otherwise
|
|
856
|
-
* @deprecated use getUser instead
|
|
857
|
-
*/
|
|
858
|
-
public async accountExists(pubkey: string | PublicKey): Promise<boolean> {
|
|
859
|
-
return !!(await this.getUser({ pubkey }));
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
/**
|
|
863
|
-
* Fetch active triggers for an account.
|
|
864
|
-
*
|
|
865
|
-
* @param accountId - Account identifier owning the triggers
|
|
866
|
-
* @throws {NordError} If no account can be resolved or the request fails.
|
|
867
|
-
*/
|
|
868
|
-
async getAccountTriggers({
|
|
869
|
-
accountId,
|
|
870
|
-
}: Readonly<{
|
|
871
|
-
accountId?: number;
|
|
872
|
-
}> = {}): Promise<AccountTriggerInfo[]> {
|
|
873
|
-
if (accountId == null) {
|
|
874
|
-
throw new NordError(
|
|
875
|
-
"Account ID is undefined. Make sure to call updateAccountId() before requesting triggers.",
|
|
876
|
-
);
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
try {
|
|
880
|
-
const triggers = await this.GET("/account/{account_id}/triggers", {
|
|
881
|
-
params: {
|
|
882
|
-
path: { account_id: accountId },
|
|
883
|
-
},
|
|
884
|
-
});
|
|
885
|
-
return triggers ?? [];
|
|
886
|
-
} catch (error) {
|
|
887
|
-
throw new NordError("Failed to fetch account triggers", { cause: error });
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
/**
|
|
892
|
-
* Fetch trigger history for an account.
|
|
893
|
-
*
|
|
894
|
-
* @param accountId - Account identifier owning the triggers
|
|
895
|
-
* @param since - RFC3339 timestamp to start from (inclusive)
|
|
896
|
-
* @param until - RFC3339 timestamp to end at (exclusive)
|
|
897
|
-
* @param pageSize - Maximum number of entries to return
|
|
898
|
-
* @param startInclusive - Pagination cursor to resume from
|
|
899
|
-
* @throws {NordError} If no account can be resolved or the request fails.
|
|
900
|
-
*/
|
|
901
|
-
async getAccountTriggerHistory({
|
|
902
|
-
accountId,
|
|
903
|
-
since,
|
|
904
|
-
until,
|
|
905
|
-
pageSize,
|
|
906
|
-
startInclusive,
|
|
907
|
-
}: Readonly<
|
|
908
|
-
HistoryTriggerQuery & { accountId?: number }
|
|
909
|
-
>): Promise<TriggerHistoryPage> {
|
|
910
|
-
if (accountId == null) {
|
|
911
|
-
throw new NordError(
|
|
912
|
-
"Account ID is undefined. Make sure to call updateAccountId() before requesting trigger history.",
|
|
913
|
-
);
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
try {
|
|
917
|
-
return await this.GET("/account/{account_id}/triggers/history", {
|
|
918
|
-
params: {
|
|
919
|
-
path: { account_id: accountId },
|
|
920
|
-
query: {
|
|
921
|
-
since,
|
|
922
|
-
until,
|
|
923
|
-
pageSize,
|
|
924
|
-
startInclusive,
|
|
925
|
-
},
|
|
926
|
-
},
|
|
927
|
-
});
|
|
928
|
-
} catch (error) {
|
|
929
|
-
throw new NordError("Failed to fetch account trigger history", {
|
|
930
|
-
cause: error,
|
|
931
|
-
});
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
}
|