@n1xyz/nord-ts 0.0.17 → 0.0.18
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/.local/qa.ts +77 -0
- package/.local/test-atomic.ts +112 -0
- package/check.sh +4 -0
- package/default.nix +47 -0
- package/dist/api/client.d.ts +14 -0
- package/dist/api/client.js +45 -0
- package/dist/gen/nord.d.ts +52 -23
- package/dist/gen/nord.js +322 -170
- package/dist/gen/openapi.d.ts +2244 -0
- package/dist/gen/openapi.js +6 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -9
- package/dist/nord/api/actions.d.ts +30 -1
- package/dist/nord/api/actions.js +60 -3
- package/dist/nord/api/core.d.ts +1 -34
- package/dist/nord/api/core.js +0 -71
- package/dist/nord/client/Nord.d.ts +31 -33
- package/dist/nord/client/Nord.js +100 -60
- package/dist/nord/client/NordUser.d.ts +64 -11
- package/dist/nord/client/NordUser.js +91 -33
- package/dist/nord/index.d.ts +0 -2
- package/dist/nord/index.js +0 -2
- package/dist/nord/models/Subscriber.d.ts +2 -2
- package/dist/types.d.ts +43 -190
- package/dist/utils.d.ts +1 -28
- package/dist/utils.js +5 -58
- package/dist/websocket/NordWebSocketClient.js +18 -13
- package/dist/websocket/index.d.ts +1 -1
- package/package.json +23 -31
- package/src/index.ts +0 -16
- package/src/nord/api/actions.ts +131 -9
- package/src/nord/api/core.ts +0 -71
- package/src/nord/client/Nord.ts +142 -76
- package/src/nord/client/NordUser.ts +171 -51
- package/src/nord/index.ts +0 -2
- package/src/nord/models/Subscriber.ts +2 -2
- package/src/types.ts +55 -216
- package/src/utils.ts +6 -63
- package/src/websocket/NordWebSocketClient.ts +23 -15
- package/src/websocket/index.ts +1 -1
- package/tests/utils.spec.ts +1 -34
- package/dist/idl/bridge.json +0 -1506
- package/jest.config.ts +0 -9
- package/nodemon.json +0 -4
- package/protoc-generate.sh +0 -23
- package/src/idl/bridge.json +0 -1506
- package/src/nord/api/market.ts +0 -122
- package/src/nord/api/queries.ts +0 -135
package/src/nord/api/actions.ts
CHANGED
|
@@ -3,15 +3,12 @@ import * as proto from "../../gen/nord";
|
|
|
3
3
|
import { FillMode, fillModeToProtoFillMode, KeyType, Side } from "../../types";
|
|
4
4
|
import {
|
|
5
5
|
assert,
|
|
6
|
-
bigIntToProtoU128,
|
|
7
6
|
BigIntValue,
|
|
8
7
|
checkedFetch,
|
|
9
8
|
checkPubKeyLength,
|
|
10
9
|
decodeLengthDelimited,
|
|
11
10
|
encodeLengthDelimited,
|
|
12
|
-
optMap,
|
|
13
11
|
SESSION_TTL,
|
|
14
|
-
toScaledU128,
|
|
15
12
|
toScaledU64,
|
|
16
13
|
} from "../../utils";
|
|
17
14
|
|
|
@@ -35,6 +32,7 @@ function makeSendHttp(
|
|
|
35
32
|
serverUrl: string,
|
|
36
33
|
): (encoded: Uint8Array) => Promise<Uint8Array> {
|
|
37
34
|
return async (body) => {
|
|
35
|
+
// TODO: this should be changed to use openapi
|
|
38
36
|
const response = await checkedFetch(`${serverUrl}/action`, {
|
|
39
37
|
method: "POST",
|
|
40
38
|
headers: {
|
|
@@ -271,15 +269,17 @@ async function placeOrderImpl(
|
|
|
271
269
|
// NOTE: if `size` equals 1.0, it will sell whole unit, for example 1.0 BTC
|
|
272
270
|
size?: Decimal.Value;
|
|
273
271
|
price?: Decimal.Value;
|
|
274
|
-
|
|
272
|
+
quoteSizeSize?: Decimal.Value;
|
|
273
|
+
quoteSizePrice?: Decimal.Value;
|
|
275
274
|
clientOrderId?: BigIntValue;
|
|
276
275
|
},
|
|
277
276
|
): Promise<bigint | undefined> {
|
|
278
277
|
const price = toScaledU64(params.price ?? 0, params.priceDecimals);
|
|
279
278
|
const size = toScaledU64(params.size ?? 0, params.sizeDecimals);
|
|
280
|
-
const quoteSize =
|
|
281
|
-
|
|
282
|
-
params.
|
|
279
|
+
const quoteSize = toScaledU64(params.quoteSizeSize ?? 0, params.sizeDecimals);
|
|
280
|
+
const quotePrice = toScaledU64(
|
|
281
|
+
params.quoteSizePrice ?? 0,
|
|
282
|
+
params.priceDecimals,
|
|
283
283
|
);
|
|
284
284
|
|
|
285
285
|
// Compose action object
|
|
@@ -297,8 +297,11 @@ async function placeOrderImpl(
|
|
|
297
297
|
isReduceOnly: params.isReduceOnly,
|
|
298
298
|
price,
|
|
299
299
|
size,
|
|
300
|
-
quoteSize:
|
|
301
|
-
clientOrderId:
|
|
300
|
+
quoteSize: { size: quoteSize, price: quotePrice },
|
|
301
|
+
clientOrderId:
|
|
302
|
+
params.clientOrderId === undefined
|
|
303
|
+
? undefined
|
|
304
|
+
: BigInt(params.clientOrderId),
|
|
302
305
|
delegatorAccountId: params.liquidateeId,
|
|
303
306
|
},
|
|
304
307
|
},
|
|
@@ -478,6 +481,124 @@ export async function transfer(
|
|
|
478
481
|
params,
|
|
479
482
|
);
|
|
480
483
|
}
|
|
484
|
+
|
|
485
|
+
export type AtomicSubaction =
|
|
486
|
+
| {
|
|
487
|
+
kind: "place";
|
|
488
|
+
// Market and order parameters – identical semantics to placeOrder()
|
|
489
|
+
marketId: number;
|
|
490
|
+
side: Side;
|
|
491
|
+
fillMode: FillMode;
|
|
492
|
+
isReduceOnly: boolean;
|
|
493
|
+
// decimals for scaling
|
|
494
|
+
sizeDecimals: number;
|
|
495
|
+
priceDecimals: number;
|
|
496
|
+
// at least one of the three has to be specified; 0 treated as "not set"
|
|
497
|
+
size?: Decimal.Value;
|
|
498
|
+
price?: Decimal.Value;
|
|
499
|
+
quoteSizeSize?: Decimal.Value;
|
|
500
|
+
quoteSizePrice?: Decimal.Value;
|
|
501
|
+
clientOrderId?: BigIntValue;
|
|
502
|
+
}
|
|
503
|
+
| {
|
|
504
|
+
kind: "cancel";
|
|
505
|
+
orderId: BigIntValue;
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
async function atomicImpl(
|
|
509
|
+
sendFn: (encoded: Uint8Array) => Promise<Uint8Array>,
|
|
510
|
+
signFn: (message: Uint8Array) => Promise<Uint8Array>,
|
|
511
|
+
currentTimestamp: bigint,
|
|
512
|
+
nonce: number,
|
|
513
|
+
params: {
|
|
514
|
+
sessionId: BigIntValue;
|
|
515
|
+
accountId?: number;
|
|
516
|
+
actions: AtomicSubaction[];
|
|
517
|
+
},
|
|
518
|
+
): Promise<proto.Receipt_AtomicResult> {
|
|
519
|
+
assert(
|
|
520
|
+
params.actions.length > 0 && params.actions.length <= 4,
|
|
521
|
+
"Atomic action must contain between 1 and 4 sub-actions",
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
const subactions: proto.AtomicSubactionKind[] = params.actions.map((a) => {
|
|
525
|
+
if (a.kind === "place") {
|
|
526
|
+
const price = toScaledU64(a.price ?? 0, a.priceDecimals);
|
|
527
|
+
const size = toScaledU64(a.size ?? 0, a.sizeDecimals);
|
|
528
|
+
const quoteSizeSize = toScaledU64(a.quoteSizeSize ?? 0, a.sizeDecimals);
|
|
529
|
+
const quoteSizePrice = toScaledU64(
|
|
530
|
+
a.quoteSizePrice ?? 0,
|
|
531
|
+
a.priceDecimals,
|
|
532
|
+
);
|
|
533
|
+
const tradeOrPlace: proto.TradeOrPlace = {
|
|
534
|
+
marketId: a.marketId,
|
|
535
|
+
orderType: {
|
|
536
|
+
side: a.side === Side.Bid ? proto.Side.BID : proto.Side.ASK,
|
|
537
|
+
fillMode: fillModeToProtoFillMode(a.fillMode),
|
|
538
|
+
isReduceOnly: a.isReduceOnly,
|
|
539
|
+
},
|
|
540
|
+
limit: {
|
|
541
|
+
price,
|
|
542
|
+
size,
|
|
543
|
+
quoteSize: { size: quoteSizeSize, price: quoteSizePrice },
|
|
544
|
+
},
|
|
545
|
+
clientOrderId:
|
|
546
|
+
a.clientOrderId === undefined ? undefined : BigInt(a.clientOrderId),
|
|
547
|
+
};
|
|
548
|
+
return {
|
|
549
|
+
inner: { $case: "tradeOrPlace", value: tradeOrPlace },
|
|
550
|
+
} as proto.AtomicSubactionKind;
|
|
551
|
+
}
|
|
552
|
+
return {
|
|
553
|
+
inner: { $case: "cancelOrder", value: { orderId: BigInt(a.orderId) } },
|
|
554
|
+
} as proto.AtomicSubactionKind;
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
const action: proto.Action = {
|
|
558
|
+
currentTimestamp,
|
|
559
|
+
nonce,
|
|
560
|
+
kind: {
|
|
561
|
+
$case: "atomic",
|
|
562
|
+
value: {
|
|
563
|
+
sessionId: BigInt(params.sessionId),
|
|
564
|
+
accountId: params.accountId, // optional
|
|
565
|
+
actions: subactions,
|
|
566
|
+
},
|
|
567
|
+
},
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
const resp = await sendAction(
|
|
571
|
+
sendFn,
|
|
572
|
+
(m) => sessionSign(signFn, m),
|
|
573
|
+
action,
|
|
574
|
+
"execute atomic action",
|
|
575
|
+
);
|
|
576
|
+
if (resp.kind?.$case === "atomic") {
|
|
577
|
+
return resp.kind.value;
|
|
578
|
+
}
|
|
579
|
+
throw new Error(`Unexpected receipt kind ${resp.kind?.$case}`);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
export async function atomic(
|
|
583
|
+
serverUrl: string,
|
|
584
|
+
signFn: (message: Uint8Array) => Promise<Uint8Array>,
|
|
585
|
+
currentTimestamp: bigint,
|
|
586
|
+
nonce: number,
|
|
587
|
+
params: {
|
|
588
|
+
sessionId: BigIntValue;
|
|
589
|
+
accountId?: number;
|
|
590
|
+
actions: AtomicSubaction[];
|
|
591
|
+
},
|
|
592
|
+
): Promise<proto.Receipt_AtomicResult> {
|
|
593
|
+
return atomicImpl(
|
|
594
|
+
makeSendHttp(serverUrl),
|
|
595
|
+
signFn,
|
|
596
|
+
currentTimestamp,
|
|
597
|
+
nonce,
|
|
598
|
+
params,
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
|
|
481
602
|
/**
|
|
482
603
|
* For testing purposes
|
|
483
604
|
*/
|
|
@@ -488,4 +609,5 @@ export const _private = {
|
|
|
488
609
|
placeOrderImpl,
|
|
489
610
|
cancelOrderImpl,
|
|
490
611
|
transferImpl,
|
|
612
|
+
atomicImpl,
|
|
491
613
|
};
|
package/src/nord/api/core.ts
CHANGED
|
@@ -3,77 +3,6 @@ import { checkedFetch } from "../../utils";
|
|
|
3
3
|
import { NordWebSocketClient } from "../../websocket/index";
|
|
4
4
|
import { NordError } from "../utils/NordError";
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* Get the current timestamp from the Nord server
|
|
8
|
-
*
|
|
9
|
-
* @param webServerUrl - Base URL for the Nord web server
|
|
10
|
-
* @returns Current timestamp as a bigint
|
|
11
|
-
* @throws {NordError} If the request fails
|
|
12
|
-
*/
|
|
13
|
-
export async function getTimestamp(webServerUrl: string): Promise<bigint> {
|
|
14
|
-
try {
|
|
15
|
-
const response = await checkedFetch(`${webServerUrl}/timestamp`);
|
|
16
|
-
return BigInt(await response.json());
|
|
17
|
-
} catch (error) {
|
|
18
|
-
throw new NordError("Failed to get timestamp", { cause: error });
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Get the next action nonce from the Nord server
|
|
24
|
-
*
|
|
25
|
-
* @param webServerUrl - Base URL for the Nord web server
|
|
26
|
-
* @returns Next action nonce
|
|
27
|
-
* @throws {NordError} If the request fails
|
|
28
|
-
*/
|
|
29
|
-
export async function getLastEventNonce(webServerUrl: string): Promise<number> {
|
|
30
|
-
try {
|
|
31
|
-
const response = await checkedFetch(
|
|
32
|
-
`${webServerUrl}/event/last-acked-nonce`,
|
|
33
|
-
);
|
|
34
|
-
const data = await response.json();
|
|
35
|
-
return data.nonce;
|
|
36
|
-
} catch (error) {
|
|
37
|
-
throw new NordError("Failed to get action nonce", { cause: error });
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Get information about the Nord server
|
|
43
|
-
*
|
|
44
|
-
* @param webServerUrl - Base URL for the Nord web server
|
|
45
|
-
* @returns Information about markets and tokens
|
|
46
|
-
* @throws {NordError} If the request fails
|
|
47
|
-
*/
|
|
48
|
-
export async function getInfo(webServerUrl: string): Promise<Info> {
|
|
49
|
-
try {
|
|
50
|
-
const response = await checkedFetch(`${webServerUrl}/info`);
|
|
51
|
-
return await response.json();
|
|
52
|
-
} catch (error) {
|
|
53
|
-
throw new NordError("Failed to get info", { cause: error });
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Get account information
|
|
59
|
-
*
|
|
60
|
-
* @param webServerUrl - Base URL for the Nord web server
|
|
61
|
-
* @param accountId - Account ID to get information for
|
|
62
|
-
* @returns Account information
|
|
63
|
-
* @throws {NordError} If the request fails
|
|
64
|
-
*/
|
|
65
|
-
export async function getAccount(
|
|
66
|
-
webServerUrl: string,
|
|
67
|
-
accountId: number,
|
|
68
|
-
): Promise<Account> {
|
|
69
|
-
try {
|
|
70
|
-
const response = await checkedFetch(`${webServerUrl}/account/${accountId}`);
|
|
71
|
-
return await response.json();
|
|
72
|
-
} catch (error) {
|
|
73
|
-
throw new NordError(`Failed to get account ${accountId}`, { cause: error });
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
6
|
/**
|
|
78
7
|
* Initialize a WebSocket client for Nord
|
|
79
8
|
*
|
package/src/nord/client/Nord.ts
CHANGED
|
@@ -1,35 +1,32 @@
|
|
|
1
1
|
import { EventEmitter } from "events";
|
|
2
|
+
import { PublicKey, Connection } from "@solana/web3.js";
|
|
3
|
+
import createClient, { Client, FetchOptions } from "openapi-fetch";
|
|
2
4
|
import {
|
|
5
|
+
Info,
|
|
3
6
|
Account,
|
|
4
|
-
ActionQuery,
|
|
5
7
|
ActionResponse,
|
|
6
|
-
ActionsResponse,
|
|
7
8
|
AggregateMetrics,
|
|
8
|
-
Info,
|
|
9
9
|
Market,
|
|
10
|
-
MarketsStatsResponse,
|
|
11
10
|
NordConfig,
|
|
12
11
|
OrderbookQuery,
|
|
13
12
|
OrderbookResponse,
|
|
14
13
|
PeakTpsPeriodUnit,
|
|
15
|
-
RollmanActionResponse,
|
|
16
|
-
RollmanActionsResponse,
|
|
17
14
|
SubscriptionPattern,
|
|
18
15
|
Token,
|
|
19
|
-
TradesQuery,
|
|
20
16
|
TradesResponse,
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
User,
|
|
18
|
+
MarketStats,
|
|
23
19
|
} from "../../types";
|
|
24
20
|
import { ProtonClient } from "@n1xyz/proton";
|
|
21
|
+
import * as proto from "../../gen/nord";
|
|
22
|
+
// import { base64 } from "@scure/base";
|
|
25
23
|
import { NordWebSocketClient } from "../../websocket/index";
|
|
26
24
|
import * as core from "../api/core";
|
|
27
|
-
import * as market from "../api/market";
|
|
28
25
|
import * as metrics from "../api/metrics";
|
|
29
|
-
import * as
|
|
26
|
+
import * as utils from "../../utils";
|
|
30
27
|
import { OrderbookSubscription, TradeSubscription } from "../models/Subscriber";
|
|
31
28
|
import { NordError } from "../utils/NordError";
|
|
32
|
-
import {
|
|
29
|
+
import type { paths } from "../../gen/openapi.ts";
|
|
33
30
|
|
|
34
31
|
/**
|
|
35
32
|
* User subscription interface
|
|
@@ -72,9 +69,12 @@ export class Nord {
|
|
|
72
69
|
/** Map of symbol to market_id */
|
|
73
70
|
private symbolToMarketId: Map<string, number> = new Map();
|
|
74
71
|
|
|
75
|
-
/** Proton client for bridge and
|
|
72
|
+
/** Proton client for bridge and hansel operations */
|
|
76
73
|
public protonClient: ProtonClient;
|
|
77
74
|
|
|
75
|
+
/** HTTP client for Nord operations */
|
|
76
|
+
private httpClient: Client<paths>;
|
|
77
|
+
|
|
78
78
|
/**
|
|
79
79
|
* Create a new Nord client
|
|
80
80
|
*
|
|
@@ -99,6 +99,7 @@ export class Nord {
|
|
|
99
99
|
this.bridgeVk = bridgeVk;
|
|
100
100
|
this.solanaUrl = solanaUrl;
|
|
101
101
|
this.protonClient = protonClient;
|
|
102
|
+
this.httpClient = createClient<paths>({ baseUrl: webServerUrl });
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
/**
|
|
@@ -162,6 +163,22 @@ export class Nord {
|
|
|
162
163
|
return core.initWebSocketClient(this.webServerUrl, subscriptions);
|
|
163
164
|
}
|
|
164
165
|
|
|
166
|
+
private async GET<P extends keyof paths & string>(
|
|
167
|
+
path: P,
|
|
168
|
+
options: FetchOptions<paths[P]["get"]>,
|
|
169
|
+
) {
|
|
170
|
+
const r = await this.httpClient.GET(path, options);
|
|
171
|
+
if (r.error) {
|
|
172
|
+
throw new NordError(`failed to GET ${path}`, { cause: r.error });
|
|
173
|
+
}
|
|
174
|
+
if (r.data === undefined) {
|
|
175
|
+
// this should never happen, but the type checker seems unhappy.
|
|
176
|
+
// if we catch this we'll need to debug accordingly.
|
|
177
|
+
throw new NordError("internal assertion violation", { cause: r });
|
|
178
|
+
}
|
|
179
|
+
return r.data;
|
|
180
|
+
}
|
|
181
|
+
|
|
165
182
|
/**
|
|
166
183
|
* Get the current timestamp from the Nord server
|
|
167
184
|
*
|
|
@@ -169,7 +186,7 @@ export class Nord {
|
|
|
169
186
|
* @throws {NordError} If the request fails
|
|
170
187
|
*/
|
|
171
188
|
async getTimestamp(): Promise<bigint> {
|
|
172
|
-
return
|
|
189
|
+
return BigInt(await this.GET("/timestamp", {}));
|
|
173
190
|
}
|
|
174
191
|
|
|
175
192
|
/**
|
|
@@ -179,7 +196,7 @@ export class Nord {
|
|
|
179
196
|
* @throws {NordError} If the request fails
|
|
180
197
|
*/
|
|
181
198
|
async getActionNonce(): Promise<number> {
|
|
182
|
-
return
|
|
199
|
+
return await this.GET("/event/last-acked-nonce", {});
|
|
183
200
|
}
|
|
184
201
|
|
|
185
202
|
/**
|
|
@@ -189,7 +206,7 @@ export class Nord {
|
|
|
189
206
|
*/
|
|
190
207
|
async fetchNordInfo(): Promise<void> {
|
|
191
208
|
try {
|
|
192
|
-
const info = await
|
|
209
|
+
const info = await this.GET("/info", {});
|
|
193
210
|
this.markets = info.markets;
|
|
194
211
|
this.tokens = info.tokens;
|
|
195
212
|
|
|
@@ -246,16 +263,6 @@ export class Nord {
|
|
|
246
263
|
await this.fetchNordInfo();
|
|
247
264
|
}
|
|
248
265
|
|
|
249
|
-
/**
|
|
250
|
-
* Get market statistics
|
|
251
|
-
*
|
|
252
|
-
* @returns Market statistics response
|
|
253
|
-
* @throws {NordError} If the request fails
|
|
254
|
-
*/
|
|
255
|
-
public async marketsStats(): Promise<MarketsStatsResponse> {
|
|
256
|
-
return market.marketsStats(this.webServerUrl);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
266
|
/**
|
|
260
267
|
* Query a specific action
|
|
261
268
|
*
|
|
@@ -263,8 +270,19 @@ export class Nord {
|
|
|
263
270
|
* @returns Action response
|
|
264
271
|
* @throws {NordError} If the request fails
|
|
265
272
|
*/
|
|
266
|
-
async queryAction(
|
|
267
|
-
|
|
273
|
+
async queryAction({
|
|
274
|
+
action_id,
|
|
275
|
+
}: {
|
|
276
|
+
action_id: number;
|
|
277
|
+
}): Promise<ActionResponse | null> {
|
|
278
|
+
return (
|
|
279
|
+
(
|
|
280
|
+
await this.queryRecentActions({
|
|
281
|
+
from: action_id,
|
|
282
|
+
to: action_id,
|
|
283
|
+
})
|
|
284
|
+
)[0] ?? null
|
|
285
|
+
);
|
|
268
286
|
}
|
|
269
287
|
|
|
270
288
|
/**
|
|
@@ -275,8 +293,23 @@ export class Nord {
|
|
|
275
293
|
* @returns Actions response
|
|
276
294
|
* @throws {NordError} If the request fails
|
|
277
295
|
*/
|
|
278
|
-
async queryRecentActions(
|
|
279
|
-
|
|
296
|
+
async queryRecentActions(query: {
|
|
297
|
+
from: number;
|
|
298
|
+
to: number;
|
|
299
|
+
}): Promise<ActionResponse[]> {
|
|
300
|
+
const xs = await this.GET("/action", {
|
|
301
|
+
params: {
|
|
302
|
+
query,
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
return xs.map((x) => ({
|
|
306
|
+
actionId: x.actionId,
|
|
307
|
+
action: utils.decodeLengthDelimited(
|
|
308
|
+
Buffer.from(x.payload, "base64"),
|
|
309
|
+
proto.Action,
|
|
310
|
+
),
|
|
311
|
+
physicalExecTime: new Date(x.physicalTime * 1000),
|
|
312
|
+
}));
|
|
280
313
|
}
|
|
281
314
|
|
|
282
315
|
/**
|
|
@@ -286,7 +319,7 @@ export class Nord {
|
|
|
286
319
|
* @throws {NordError} If the request fails
|
|
287
320
|
*/
|
|
288
321
|
async getLastActionId(): Promise<number> {
|
|
289
|
-
return
|
|
322
|
+
return await this.GET("/action/last-executed-id", {});
|
|
290
323
|
}
|
|
291
324
|
|
|
292
325
|
/**
|
|
@@ -351,28 +384,6 @@ export class Nord {
|
|
|
351
384
|
return metrics.getTotalTransactions(this.webServerUrl);
|
|
352
385
|
}
|
|
353
386
|
|
|
354
|
-
/**
|
|
355
|
-
* Query an action from Rollman
|
|
356
|
-
*
|
|
357
|
-
* @param query - Action query parameters
|
|
358
|
-
* @returns Rollman action response
|
|
359
|
-
* @throws {NordError} If the request fails
|
|
360
|
-
*/
|
|
361
|
-
async actionQueryRollman(query: ActionQuery): Promise<RollmanActionResponse> {
|
|
362
|
-
return queries.actionQueryRollman(this.webServerUrl, query);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Query actions from Rollman
|
|
367
|
-
*
|
|
368
|
-
* @param last_n - Number of recent actions to query
|
|
369
|
-
* @returns Rollman actions response
|
|
370
|
-
* @throws {NordError} If the request fails
|
|
371
|
-
*/
|
|
372
|
-
async actionsQueryRollman(last_n: number): Promise<RollmanActionsResponse> {
|
|
373
|
-
return queries.actionsQueryRollman(this.webServerUrl, last_n);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
387
|
/**
|
|
377
388
|
* Query Prometheus metrics
|
|
378
389
|
*
|
|
@@ -511,8 +522,38 @@ export class Nord {
|
|
|
511
522
|
* @returns Trades response
|
|
512
523
|
* @throws {NordError} If the request fails
|
|
513
524
|
*/
|
|
514
|
-
public async getTrades(
|
|
515
|
-
|
|
525
|
+
public async getTrades(
|
|
526
|
+
query: Readonly<{
|
|
527
|
+
marketId?: number;
|
|
528
|
+
takerId?: number;
|
|
529
|
+
makerId?: number;
|
|
530
|
+
takerSide?: "bid" | "ask";
|
|
531
|
+
pageSize?: number;
|
|
532
|
+
sinceRcf3339?: string;
|
|
533
|
+
untilRfc3339?: string;
|
|
534
|
+
pageId?: string;
|
|
535
|
+
}>,
|
|
536
|
+
): Promise<TradesResponse> {
|
|
537
|
+
if (query.sinceRcf3339 && !utils.isRfc3339(query.sinceRcf3339)) {
|
|
538
|
+
throw new NordError(`Invalid RFC3339 timestamp: ${query.sinceRcf3339}`);
|
|
539
|
+
}
|
|
540
|
+
if (query.untilRfc3339 && !utils.isRfc3339(query.untilRfc3339)) {
|
|
541
|
+
throw new NordError(`Invalid RFC3339 timestamp: ${query.untilRfc3339}`);
|
|
542
|
+
}
|
|
543
|
+
return await this.GET("/trades", {
|
|
544
|
+
params: {
|
|
545
|
+
query: {
|
|
546
|
+
takerId: query.takerId,
|
|
547
|
+
makerId: query.makerId,
|
|
548
|
+
marketId: query.marketId,
|
|
549
|
+
pageSize: query.pageSize,
|
|
550
|
+
takerSide: query.takerSide,
|
|
551
|
+
since: query.sinceRcf3339,
|
|
552
|
+
until: query.untilRfc3339,
|
|
553
|
+
startInclusive: query.pageId,
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
});
|
|
516
557
|
}
|
|
517
558
|
|
|
518
559
|
/**
|
|
@@ -522,10 +563,18 @@ export class Nord {
|
|
|
522
563
|
* @returns User account IDs response
|
|
523
564
|
* @throws {NordError} If the request fails
|
|
524
565
|
*/
|
|
525
|
-
public async
|
|
526
|
-
|
|
527
|
-
): Promise<
|
|
528
|
-
|
|
566
|
+
public async getUser(query: {
|
|
567
|
+
pubkey: string | PublicKey;
|
|
568
|
+
}): Promise<User | null> {
|
|
569
|
+
const r = await this.httpClient.GET("/user/{pubkey}", {
|
|
570
|
+
params: {
|
|
571
|
+
path: { pubkey: query.pubkey.toString() },
|
|
572
|
+
},
|
|
573
|
+
});
|
|
574
|
+
if (r.response.status === 404) {
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
return r.data!;
|
|
529
578
|
}
|
|
530
579
|
|
|
531
580
|
/**
|
|
@@ -539,21 +588,31 @@ export class Nord {
|
|
|
539
588
|
*/
|
|
540
589
|
public async getOrderbook(query: OrderbookQuery): Promise<OrderbookResponse> {
|
|
541
590
|
// If only symbol is provided, convert it to market_id
|
|
591
|
+
let marketId: number;
|
|
542
592
|
if (query.symbol && query.market_id === undefined) {
|
|
543
593
|
// If the map is empty, try to fetch market information first
|
|
544
594
|
if (this.symbolToMarketId.size === 0) {
|
|
545
595
|
await this.fetchNordInfo();
|
|
546
596
|
}
|
|
547
597
|
|
|
548
|
-
const
|
|
549
|
-
if (
|
|
598
|
+
const id = this.symbolToMarketId.get(query.symbol);
|
|
599
|
+
if (id === undefined) {
|
|
550
600
|
throw new NordError(`Unknown market symbol: ${query.symbol}`);
|
|
551
601
|
}
|
|
552
|
-
|
|
553
|
-
|
|
602
|
+
marketId = id;
|
|
603
|
+
} else if (query.market_id !== undefined) {
|
|
604
|
+
marketId = query.market_id;
|
|
605
|
+
} else {
|
|
606
|
+
throw new NordError(
|
|
607
|
+
"Either symbol or market_id must be provided for orderbook query",
|
|
608
|
+
);
|
|
554
609
|
}
|
|
555
610
|
|
|
556
|
-
return
|
|
611
|
+
return await this.GET("/market/{market_id}/orderbook", {
|
|
612
|
+
params: {
|
|
613
|
+
path: { market_id: marketId },
|
|
614
|
+
},
|
|
615
|
+
});
|
|
557
616
|
}
|
|
558
617
|
|
|
559
618
|
/**
|
|
@@ -563,7 +622,7 @@ export class Nord {
|
|
|
563
622
|
* @throws {NordError} If the request fails
|
|
564
623
|
*/
|
|
565
624
|
public async getInfo(): Promise<Info> {
|
|
566
|
-
return
|
|
625
|
+
return await this.GET("/info", {});
|
|
567
626
|
}
|
|
568
627
|
|
|
569
628
|
/**
|
|
@@ -574,17 +633,28 @@ export class Nord {
|
|
|
574
633
|
* @throws {NordError} If the request fails
|
|
575
634
|
*/
|
|
576
635
|
public async getAccount(accountId: number): Promise<Account> {
|
|
577
|
-
return
|
|
636
|
+
return await this.GET("/account/{account_id}", {
|
|
637
|
+
params: {
|
|
638
|
+
path: { account_id: accountId },
|
|
639
|
+
},
|
|
640
|
+
});
|
|
578
641
|
}
|
|
579
642
|
|
|
580
643
|
/**
|
|
581
644
|
* Get market statistics (alias for marketsStats for backward compatibility)
|
|
582
645
|
*
|
|
583
|
-
* @deprecated Use marketsStats instead
|
|
584
646
|
* @returns Market statistics response
|
|
585
647
|
*/
|
|
586
|
-
public async getMarketStats(
|
|
587
|
-
|
|
648
|
+
public async getMarketStats({
|
|
649
|
+
marketId,
|
|
650
|
+
}: {
|
|
651
|
+
marketId: number;
|
|
652
|
+
}): Promise<MarketStats> {
|
|
653
|
+
return await this.GET("/market/{market_id}/stats", {
|
|
654
|
+
params: {
|
|
655
|
+
path: { market_id: marketId },
|
|
656
|
+
},
|
|
657
|
+
});
|
|
588
658
|
}
|
|
589
659
|
|
|
590
660
|
/**
|
|
@@ -592,13 +662,9 @@ export class Nord {
|
|
|
592
662
|
*
|
|
593
663
|
* @param address - The public key address to check
|
|
594
664
|
* @returns True if the account exists, false otherwise
|
|
665
|
+
* @deprecated use getUser instead
|
|
595
666
|
*/
|
|
596
|
-
public async accountExists(
|
|
597
|
-
|
|
598
|
-
await market.getUserAccountIds(this.webServerUrl, { pubkey: address });
|
|
599
|
-
return true;
|
|
600
|
-
} catch {
|
|
601
|
-
return false;
|
|
602
|
-
}
|
|
667
|
+
public async accountExists(pubkey: string | PublicKey): Promise<boolean> {
|
|
668
|
+
return !!(await this.getUser({ pubkey }));
|
|
603
669
|
}
|
|
604
670
|
}
|