@n1xyz/nord-ts 0.0.18-8121ed05.0 → 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.
Files changed (48) hide show
  1. package/.local/qa.ts +77 -0
  2. package/.local/test-atomic.ts +112 -0
  3. package/check.sh +4 -0
  4. package/default.nix +47 -0
  5. package/dist/api/client.d.ts +14 -0
  6. package/dist/api/client.js +45 -0
  7. package/dist/gen/nord.d.ts +52 -23
  8. package/dist/gen/nord.js +322 -170
  9. package/dist/gen/openapi.d.ts +2244 -0
  10. package/dist/gen/openapi.js +6 -0
  11. package/dist/index.d.ts +0 -1
  12. package/dist/index.js +0 -9
  13. package/dist/nord/api/actions.d.ts +30 -1
  14. package/dist/nord/api/actions.js +60 -3
  15. package/dist/nord/api/core.d.ts +1 -34
  16. package/dist/nord/api/core.js +0 -71
  17. package/dist/nord/client/Nord.d.ts +31 -33
  18. package/dist/nord/client/Nord.js +100 -60
  19. package/dist/nord/client/NordUser.d.ts +64 -11
  20. package/dist/nord/client/NordUser.js +90 -33
  21. package/dist/nord/index.d.ts +0 -2
  22. package/dist/nord/index.js +0 -2
  23. package/dist/nord/models/Subscriber.d.ts +2 -2
  24. package/dist/types.d.ts +43 -190
  25. package/dist/utils.d.ts +1 -19
  26. package/dist/utils.js +5 -39
  27. package/dist/websocket/NordWebSocketClient.js +18 -13
  28. package/dist/websocket/index.d.ts +1 -1
  29. package/package.json +20 -27
  30. package/src/index.ts +0 -16
  31. package/src/nord/api/actions.ts +131 -9
  32. package/src/nord/api/core.ts +0 -71
  33. package/src/nord/client/Nord.ts +142 -76
  34. package/src/nord/client/NordUser.ts +171 -50
  35. package/src/nord/index.ts +0 -2
  36. package/src/nord/models/Subscriber.ts +2 -2
  37. package/src/types.ts +55 -216
  38. package/src/utils.ts +6 -42
  39. package/src/websocket/NordWebSocketClient.ts +23 -15
  40. package/src/websocket/index.ts +1 -1
  41. package/tests/utils.spec.ts +1 -34
  42. package/dist/idl/bridge.json +0 -1506
  43. package/jest.config.ts +0 -9
  44. package/nodemon.json +0 -4
  45. package/protoc-generate.sh +0 -23
  46. package/src/idl/bridge.json +0 -1506
  47. package/src/nord/api/market.ts +0 -122
  48. package/src/nord/api/queries.ts +0 -135
package/.local/qa.ts ADDED
@@ -0,0 +1,77 @@
1
+ // quick script i use to qa sdk against deployed instance. this
2
+ // will no longer be needed once we have proper integration tests.
3
+
4
+ import { Nord, NordUser } from "../src";
5
+
6
+ const nord = await Nord.initNord({
7
+ bridgeVk: "rbrpQMMPaVLSG3hTPZj2GLKxGLbPM2q7gDxc7BTync6",
8
+ initWebSockets: false,
9
+ solanaUrl: "https://api.devnet.solana.com",
10
+ webServerUrl: "https://zo-devnet-rc.n1.xyz",
11
+ });
12
+
13
+ const poky = await nord.getUser({
14
+ pubkey: "GSPX6qX4bs8oTU3n5A1Xx8q9MPiGFyxWhMtuVvpZNK52",
15
+ });
16
+
17
+ console.log("user", poky);
18
+
19
+ if (!poky) {
20
+ throw new Error("No user found");
21
+ }
22
+
23
+ console.log("account", await nord.getAccount(poky.accountIds[0]));
24
+ console.log("timestamp", await nord.getTimestamp());
25
+ console.log("actionNonce", await nord.getActionNonce());
26
+ console.log("lastActionId", await nord.getLastActionId());
27
+ console.log("info", await nord.getInfo());
28
+
29
+ console.log(
30
+ "marketStats",
31
+ await nord.getMarketStats({
32
+ marketId: 1,
33
+ }),
34
+ );
35
+
36
+ console.log(
37
+ "orderbook",
38
+ await nord.getOrderbook({
39
+ market_id: 1,
40
+ }),
41
+ );
42
+
43
+ const since = new Date();
44
+ since.setMinutes(0);
45
+
46
+ console.log(
47
+ "trades",
48
+ await nord.getTrades({
49
+ takerId: poky.accountIds[0],
50
+ sinceRcf3339: since.toISOString(),
51
+ untilRfc3339: new Date().toISOString(),
52
+ pageId: "0",
53
+ }),
54
+ );
55
+
56
+ console.log(
57
+ "recentActions",
58
+ await nord.queryRecentActions({
59
+ from: 6,
60
+ to: 6,
61
+ }),
62
+ );
63
+
64
+ console.log("action", await nord.queryAction({ action_id: 0 }));
65
+ console.log("action", await nord.queryAction({ action_id: 1 }));
66
+ console.log("action", await nord.queryAction({ action_id: 2 }));
67
+ console.log("action", await nord.queryAction({ action_id: 5 }));
68
+ console.log("action", await nord.queryAction({ action_id: 100 }));
69
+
70
+ const user = NordUser.fromPrivateKey(
71
+ nord,
72
+ new Uint8Array(JSON.parse(process.env.PRIVATE_KEY!)),
73
+ );
74
+
75
+ console.log("deposit", await user.deposit({ amount: 2.0, tokenId: 0 }));
76
+ await user.refreshSession();
77
+ console.log("withdraw", await user.withdraw({ amount: 2.0, tokenId: 0 }));
@@ -0,0 +1,112 @@
1
+ import { NordWebSocketClient } from "../src/websocket/NordWebSocketClient";
2
+
3
+ /**
4
+ * All event types that we track in the counters object. Keeping this
5
+ * union type explicit lets TypeScript validate that we use the same
6
+ * keys everywhere and prevents typos.
7
+ */
8
+ export type CounterKeys =
9
+ | "trade"
10
+ | "delta"
11
+ | "account"
12
+ | "error"
13
+ | "disconnected";
14
+
15
+ interface Endpoint {
16
+ /** WebSocket endpoint URL */
17
+ url: string;
18
+ /** Streams we want to subscribe to once connected (can be empty) */
19
+ streams: string[];
20
+ }
21
+
22
+ /**
23
+ * Web‑socket endpoints to test. Keep this list short & focused so the smoke
24
+ * test finishes quickly inside CI.
25
+ */
26
+ const endpoints: Endpoint[] = [
27
+ // {
28
+ // url: "wss://staging.n1.xyz/ws/",
29
+ // streams: [], // no runtime subscribe call needed
30
+ // },
31
+
32
+ // {
33
+ // url: `wss://staging.n1.xyz/ws/account@${process.env.NORD_ACCOUNT_ID ?? "3"}`,
34
+ // streams: [],
35
+ // },
36
+
37
+ {
38
+ url: `wss://zo-devnet-rc.n1.xyz/ws/trades@ETHPERP`,
39
+ streams: ["trades@ETHPERP"],
40
+ },
41
+ ];
42
+
43
+ /** How long each endpoint should be observed for (default: 15 s) */
44
+ // const TEST_DURATION_MS = Number(process.env.TEST_DURATION_MS ?? 15_000);
45
+ const TEST_DURATION_MS = 15_000;
46
+
47
+ /**
48
+ * Simple message counters for every event type the client can emit.
49
+ * This is mutated by the event handlers during the test run.
50
+ */
51
+ const counters: Record<CounterKeys, number> = {
52
+ trade: 0,
53
+ delta: 0,
54
+ account: 0,
55
+ error: 0,
56
+ disconnected: 0,
57
+ };
58
+
59
+ /**
60
+ * Connect to a single endpoint, wait for activity, then verify we received the
61
+ * expected events.
62
+ */
63
+ async function smokeTest(endpoint: Endpoint): Promise<void> {
64
+ console.log(`🛰️ Connecting to ${endpoint.url} …`);
65
+ const client = new NordWebSocketClient(endpoint.url);
66
+ client.connect();
67
+
68
+ client.on("connected", () => {
69
+ console.log("✅ connected");
70
+ });
71
+
72
+ client.on("disconnected", () => {
73
+ console.log("⚠️ disconnected");
74
+ counters.disconnected++;
75
+ });
76
+
77
+ client.on("error", (err: unknown) => {
78
+ console.error("❌ error:", err);
79
+ counters.error++;
80
+ });
81
+
82
+ client.on("trades", (m) => {
83
+ console.log({ m });
84
+ counters.trade++;
85
+ });
86
+ client.on("deltas", (m) => {
87
+ console.log({ m });
88
+ counters.delta++;
89
+ });
90
+ client.on("accounts", (m) => {
91
+ console.log({ m });
92
+ counters.account++;
93
+ });
94
+
95
+ // Wait a bit, then close and report
96
+ await new Promise((resolve) => setTimeout(resolve, TEST_DURATION_MS));
97
+ client.close();
98
+ }
99
+
100
+ (async () => {
101
+ try {
102
+ for (const ep of endpoints) {
103
+ await smokeTest(ep);
104
+ }
105
+ console.log("\n🎉 All websocket endpoints responded as expected:");
106
+ console.table(counters);
107
+ } catch (e) {
108
+ console.error("\n🔴 Smoke test failed:", (e as Error).message);
109
+ console.table(counters);
110
+ process.exitCode = 1;
111
+ }
112
+ })();
package/check.sh ADDED
@@ -0,0 +1,4 @@
1
+ npm install
2
+ npm build
3
+ npm run test
4
+ npm run ci
package/default.nix ADDED
@@ -0,0 +1,47 @@
1
+ { inputs, ... }:
2
+ {
3
+ perSystem =
4
+ {
5
+ pkgs,
6
+ self',
7
+ system,
8
+ ...
9
+ }:
10
+ {
11
+ packages = {
12
+ nord-ts-proto =
13
+ let
14
+ ts-proto = inputs.n1.packages.${system}.ts-proto;
15
+ in
16
+ pkgs.writeShellApplication {
17
+ name = "nord-ts-proto";
18
+ runtimeInputs = [ pkgs.protobuf ];
19
+ text = ''
20
+ if [ $# -ne 2 ]; then
21
+ echo >&2 "usage: $(basename "$0") <proto-file> <out-dir>"
22
+ exit 1
23
+ fi
24
+ readonly PROTO_FILE="$1"
25
+ readonly OUT_DIR="$2"
26
+ protoc \
27
+ --plugin="${ts-proto}/bin/protoc-gen-ts_proto" \
28
+ --ts_proto_opt=forceLong=bigint \
29
+ --ts_proto_opt=esModuleInterop=true \
30
+ --ts_proto_opt=oneof=unions-value \
31
+ --ts_proto_opt=unrecognizedEnum=false \
32
+ --ts_proto_out="$OUT_DIR" \
33
+ --proto_path="$(dirname "$PROTO_FILE")" \
34
+ "$PROTO_FILE"
35
+ '';
36
+ };
37
+ nord-ts-check = pkgs.writeShellApplication {
38
+ name = "ts-check";
39
+ runtimeInputs = [
40
+ pkgs.protobuf
41
+ pkgs.pnpm
42
+ ];
43
+ text = builtins.readFile ./check.sh;
44
+ };
45
+ };
46
+ };
47
+ }
@@ -0,0 +1,14 @@
1
+ import type { paths } from "../gen/openapi";
2
+ export interface ClientConfig {
3
+ baseUrl: string;
4
+ }
5
+ export declare function createApiClient(config: ClientConfig): import("openapi-fetch").Client<paths, `${string}/${string}`>;
6
+ export declare function handleApiResponse<T>(response: Promise<{
7
+ data: T;
8
+ error?: unknown;
9
+ response: Response;
10
+ } | {
11
+ data?: never;
12
+ error?: unknown;
13
+ response: Response;
14
+ }>): Promise<T>;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createApiClient = createApiClient;
7
+ exports.handleApiResponse = handleApiResponse;
8
+ const openapi_fetch_1 = __importDefault(require("openapi-fetch"));
9
+ const NordError_1 = require("../nord/utils/NordError");
10
+ function createApiClient(config) {
11
+ const client = (0, openapi_fetch_1.default)({
12
+ baseUrl: config.baseUrl,
13
+ });
14
+ client.use({
15
+ onResponse({ response }) {
16
+ if (!response.ok) {
17
+ throw new NordError_1.NordError(`HTTP ${response.status}: ${response.statusText}`, {
18
+ statusCode: response.status,
19
+ details: { url: response.url }
20
+ });
21
+ }
22
+ return response;
23
+ },
24
+ });
25
+ return client;
26
+ }
27
+ async function handleApiResponse(response) {
28
+ const result = await response;
29
+ if (result.error || !result.response.ok) {
30
+ const errorMessage = result.error
31
+ ? `HTTP ${result.response.status}: ${result.response.statusText}`
32
+ : `Request failed: ${result.response.statusText}`;
33
+ throw new NordError_1.NordError(errorMessage, {
34
+ statusCode: result.response.status,
35
+ details: { url: result.response.url }
36
+ });
37
+ }
38
+ if (!('data' in result)) {
39
+ throw new NordError_1.NordError("No data in response", {
40
+ statusCode: 500,
41
+ details: { url: result.response.url }
42
+ });
43
+ }
44
+ return result.data;
45
+ }
@@ -181,6 +181,7 @@ export declare enum Error {
181
181
  POSITION_STATE_ORDER_PRICE = 203,
182
182
  POSITION_STATE_ORDER_SIZE = 204,
183
183
  POSITION_STATE_ORDER_SIDE = 205,
184
+ /** POSITION_SIZE_LIMIT - 1100_1110 */
184
185
  POSITION_SIZE_LIMIT = 206,
185
186
  PRICE = 209,
186
187
  SIGNATURE_VERIFICATION = 217,
@@ -194,11 +195,22 @@ export declare enum Error {
194
195
  ORDER_EXECUTION_MISSING_LIMITS = 243,
195
196
  ORDER_EXECUTION_MISSING_PRICE = 244,
196
197
  ORDER_EXECUTION_SIZE_LIMIT = 245,
198
+ /** ORDER_EXECUTION_LIMIT_PRICE - 111_0110 */
197
199
  ORDER_EXECUTION_LIMIT_PRICE = 246,
198
- /** ORDER_REDUCE_IS_POST_ONLY - Reduce orders can only be post only. */
200
+ /**
201
+ * ORDER_REDUCE_IS_POST_ONLY - Reduce orders can only be post only.
202
+ * 01111_0111 = 247
203
+ */
199
204
  ORDER_REDUCE_IS_POST_ONLY = 247,
200
- /** ORDER_EXECUTION_SELL_PRICE - Order was rejected, because it planned under proposed price */
205
+ /**
206
+ * ORDER_EXECUTION_SELL_PRICE - Order was rejected, because it planned under proposed price
207
+ * 01111_1000 = 248
208
+ */
201
209
  ORDER_EXECUTION_SELL_PRICE = 248,
210
+ /** ATOMICS_TRADES_CANNOT_FOLLOW_PLACES - 10000_0000 */
211
+ ATOMICS_TRADES_CANNOT_FOLLOW_PLACES = 256,
212
+ /** ATOMICS_CANCELS_CANNOT_FOLLOW_TRADES_PLACES - 10000_0001 */
213
+ ATOMICS_CANCELS_CANNOT_FOLLOW_TRADES_PLACES = 257,
202
214
  NOT_IMPLEMENTED = 500,
203
215
  Dropped = 999
204
216
  }
@@ -209,13 +221,6 @@ export declare enum SpecialAccount {
209
221
  }
210
222
  export declare function specialAccountFromJSON(object: any): SpecialAccount;
211
223
  export declare function specialAccountToJSON(object: SpecialAccount): string;
212
- /** Emulation of 128-bit integers since Protobuf doesn't have native support */
213
- export interface U128 {
214
- /** Low 64-bit word */
215
- lo: bigint;
216
- /** High 64-bit word */
217
- hi: bigint;
218
- }
219
224
  export interface Market {
220
225
  marketId: number;
221
226
  priceDecimals: number;
@@ -328,8 +333,8 @@ export interface Action {
328
333
  $case: "takePosition";
329
334
  value: Action_TakePosition;
330
335
  } | {
331
- $case: "transaction";
332
- value: Transaction;
336
+ $case: "atomic";
337
+ value: Atomic;
333
338
  } | undefined;
334
339
  }
335
340
  export interface Action_CreateSession {
@@ -445,12 +450,8 @@ export interface Action_PlaceOrder {
445
450
  * `1` here equals to market's `10^-(size_decimals + price_decimals)`
446
451
  * When used in token balances and reward computations, shifted by market's
447
452
  * `size_decimals + price_decimals`. Optional, treated as not set if 0.
448
- *
449
- * NB: Requires 128-bit value because it can be seen as sum
450
- * of order sizes multiplied by respective prices. Since size is up to 48
451
- * bits and price is up to 64 bits, their product can easily exceed 64 bits.
452
453
  */
453
- quoteSize: U128 | undefined;
454
+ quoteSize: QuoteSize | undefined;
454
455
  /**
455
456
  * Optional account on behalf of whom the order should be placed.
456
457
  * Executed only if sender has delegated authority to do so,
@@ -471,6 +472,11 @@ export interface Action_PlaceOrder {
471
472
  * if not specified, first account of session's owner user is picked
472
473
  */
473
474
  senderAccountId?: number | undefined;
475
+ /**
476
+ * Opaque identifier chosen by the sender to be able to correlate this
477
+ * place order requests with the corresponding fill and place order events.
478
+ */
479
+ senderTrackingId?: bigint | undefined;
474
480
  }
475
481
  export interface Action_CancelOrderById {
476
482
  sessionId: bigint;
@@ -584,7 +590,7 @@ export interface Action_TakePosition {
584
590
  */
585
591
  price?: bigint | undefined;
586
592
  }
587
- export interface TransactionKind {
593
+ export interface AtomicSubactionKind {
588
594
  inner?: {
589
595
  $case: "tradeOrPlace";
590
596
  value: TradeOrPlace;
@@ -593,10 +599,10 @@ export interface TransactionKind {
593
599
  value: CancelOrder;
594
600
  } | undefined;
595
601
  }
596
- export interface Transaction {
602
+ export interface Atomic {
597
603
  sessionId: bigint;
598
- accountId?: bigint | undefined;
599
- actions: TransactionKind[];
604
+ accountId?: number | undefined;
605
+ actions: AtomicSubactionKind[];
600
606
  }
601
607
  export interface Receipt {
602
608
  /**
@@ -662,6 +668,9 @@ export interface Receipt {
662
668
  } | {
663
669
  $case: "positionTakenOrTraded";
664
670
  value: Receipt_PositionTakenOrTradedResult;
671
+ } | {
672
+ $case: "atomic";
673
+ value: Receipt_AtomicResult;
665
674
  } | undefined;
666
675
  }
667
676
  export interface Receipt_Posted {
@@ -686,6 +695,7 @@ export interface Receipt_PlaceOrderResult {
686
695
  posted?: Receipt_Posted | undefined;
687
696
  fills: Receipt_Trade[];
688
697
  clientOrderId?: bigint | undefined;
698
+ senderTrackingId?: bigint | undefined;
689
699
  }
690
700
  export interface Receipt_TakenResult {
691
701
  pnl: bigint;
@@ -774,7 +784,24 @@ export interface Receipt_TriggerAdded {
774
784
  }
775
785
  export interface Receipt_TriggerRemoved {
776
786
  }
777
- export declare const U128: MessageFns<U128>;
787
+ export interface Receipt_AtomicSubactionResultKind {
788
+ inner?: //
789
+ /**
790
+ * reusing existing messages -> way less code to change in nord, with some
791
+ * duplication of data unlike input, which is required to be very specific
792
+ * to be correct, receipts can be same to easy ingested into view and hist
793
+ */
794
+ {
795
+ $case: "placeOrderResult";
796
+ value: Receipt_PlaceOrderResult;
797
+ } | {
798
+ $case: "cancelOrder";
799
+ value: Receipt_CancelOrderResult;
800
+ } | undefined;
801
+ }
802
+ export interface Receipt_AtomicResult {
803
+ results: Receipt_AtomicSubactionResultKind[];
804
+ }
778
805
  export declare const Market: MessageFns<Market>;
779
806
  export declare const Token: MessageFns<Token>;
780
807
  export declare const QuoteSize: MessageFns<QuoteSize>;
@@ -801,8 +828,8 @@ export declare const Action_Transfer: MessageFns<Action_Transfer>;
801
828
  export declare const Action_AddTrigger: MessageFns<Action_AddTrigger>;
802
829
  export declare const Action_RemoveTrigger: MessageFns<Action_RemoveTrigger>;
803
830
  export declare const Action_TakePosition: MessageFns<Action_TakePosition>;
804
- export declare const TransactionKind: MessageFns<TransactionKind>;
805
- export declare const Transaction: MessageFns<Transaction>;
831
+ export declare const AtomicSubactionKind: MessageFns<AtomicSubactionKind>;
832
+ export declare const Atomic: MessageFns<Atomic>;
806
833
  export declare const Receipt: MessageFns<Receipt>;
807
834
  export declare const Receipt_Posted: MessageFns<Receipt_Posted>;
808
835
  export declare const Receipt_Trade: MessageFns<Receipt_Trade>;
@@ -826,6 +853,8 @@ export declare const Receipt_Unpaused: MessageFns<Receipt_Unpaused>;
826
853
  export declare const Receipt_Transferred: MessageFns<Receipt_Transferred>;
827
854
  export declare const Receipt_TriggerAdded: MessageFns<Receipt_TriggerAdded>;
828
855
  export declare const Receipt_TriggerRemoved: MessageFns<Receipt_TriggerRemoved>;
856
+ export declare const Receipt_AtomicSubactionResultKind: MessageFns<Receipt_AtomicSubactionResultKind>;
857
+ export declare const Receipt_AtomicResult: MessageFns<Receipt_AtomicResult>;
829
858
  type Builtin = Date | Function | Uint8Array | string | number | boolean | bigint | undefined;
830
859
  export type DeepPartial<T> = T extends Builtin ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : T extends {
831
860
  $case: string;