@n1xyz/nord-ts 0.0.21 → 0.0.22

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 (45) 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/gen/common.d.ts +68 -0
  14. package/dist/gen/common.js +215 -0
  15. package/dist/gen/nord_pb.d.ts +3651 -0
  16. package/dist/gen/nord_pb.js +892 -0
  17. package/dist/gen/openapi.d.ts +241 -2
  18. package/dist/idl/bridge.d.ts +569 -0
  19. package/dist/idl/bridge.js +8 -0
  20. package/dist/idl/bridge.json +1506 -0
  21. package/dist/idl/index.d.ts +607 -0
  22. package/dist/idl/index.js +8 -0
  23. package/dist/nord/api/actions.d.ts +30 -72
  24. package/dist/nord/api/actions.js +179 -200
  25. package/dist/nord/api/market.d.ts +36 -0
  26. package/dist/nord/api/market.js +96 -0
  27. package/dist/nord/api/queries.d.ts +46 -0
  28. package/dist/nord/api/queries.js +109 -0
  29. package/dist/nord/client/Nord.js +3 -3
  30. package/dist/nord/client/NordUser.d.ts +26 -13
  31. package/dist/nord/client/NordUser.js +13 -10
  32. package/dist/types.d.ts +12 -1
  33. package/dist/types.js +29 -2
  34. package/dist/utils.d.ts +6 -20
  35. package/dist/utils.js +17 -35
  36. package/dist/websocket/NordWebSocketClient.js +2 -6
  37. package/package.json +26 -23
  38. package/src/gen/nord_pb.ts +4172 -0
  39. package/src/gen/openapi.ts +241 -2
  40. package/src/nord/api/actions.ts +249 -370
  41. package/src/nord/client/Nord.ts +3 -3
  42. package/src/nord/client/NordUser.ts +40 -19
  43. package/src/types.ts +32 -1
  44. package/src/utils.ts +24 -43
  45. package/src/websocket/NordWebSocketClient.ts +2 -8
@@ -2,7 +2,7 @@ import { ProtonClient } from "@n1xyz/proton";
2
2
  import { Connection, PublicKey } from "@solana/web3.js";
3
3
  import { EventEmitter } from "events";
4
4
  import createClient, { Client, FetchOptions } from "openapi-fetch";
5
- import * as proto from "../../gen/nord";
5
+ import * as proto from "../../gen/nord_pb";
6
6
  import type { paths } from "../../gen/openapi.ts";
7
7
  import {
8
8
  Account,
@@ -305,9 +305,9 @@ export class Nord {
305
305
  actionId: x.actionId,
306
306
  action: utils.decodeLengthDelimited(
307
307
  Buffer.from(x.payload, "base64"),
308
- proto.Action,
308
+ proto.ActionSchema,
309
309
  ),
310
- physicalExecTime: new Date(x.physicalTime * 1000),
310
+ physicalExecTime: new Date(x.physicalTime),
311
311
  }));
312
312
  }
313
313
 
@@ -4,14 +4,19 @@ import {
4
4
  TOKEN_PROGRAM_ID,
5
5
  TOKEN_2022_PROGRAM_ID,
6
6
  } from "@solana/spl-token";
7
- import { Connection, PublicKey, Transaction } from "@solana/web3.js";
7
+ import {
8
+ Connection,
9
+ PublicKey,
10
+ Transaction,
11
+ SendOptions,
12
+ } from "@solana/web3.js";
8
13
  import Decimal from "decimal.js";
9
14
  import * as ed from "@noble/ed25519";
10
15
  import { sha512 } from "@noble/hashes/sha512";
11
16
  ed.etc.sha512Sync = sha512;
12
17
  import { floatToScaledBigIntLossy } from "@n1xyz/proton";
13
- import { FillMode, Order, Side, SPLTokenInfo } from "../../types";
14
- import * as proto from "../../gen/nord";
18
+ import { FillMode, Side, SPLTokenInfo, QuoteSize } from "../../types";
19
+ import * as proto from "../../gen/nord_pb";
15
20
  import {
16
21
  BigIntValue,
17
22
  checkedFetch,
@@ -87,8 +92,8 @@ export interface PlaceOrderParams {
87
92
  /** Order price */
88
93
  price?: Decimal.Value;
89
94
 
90
- /** Quote size (for market orders) */
91
- quoteSize?: Decimal.Value;
95
+ /** Quote size object (requires non-zero price and size) */
96
+ quoteSize?: QuoteSize;
92
97
 
93
98
  /** Account ID to place the order from */
94
99
  accountId?: number;
@@ -142,8 +147,8 @@ export interface UserAtomicSubaction {
142
147
  /** Order price */
143
148
  price?: Decimal.Value;
144
149
 
145
- /** Quote size (for market orders) */
146
- quoteSize?: Decimal.Value;
150
+ /** Quote size object (for market-style placement) */
151
+ quoteSize?: QuoteSize;
147
152
 
148
153
  /** The client order ID of the order. */
149
154
  clientOrderId?: BigIntValue;
@@ -471,6 +476,7 @@ export class NordUser {
471
476
  * @param amount - Amount to deposit
472
477
  * @param tokenId - Token ID
473
478
  * @param recipient - Recipient address; defaults to the user's address
479
+ * @param sendOptions - Send options for .sendTransaction
474
480
  * @returns Transaction signature
475
481
  * @throws {NordError} If required parameters are missing or operation fails
476
482
  */
@@ -478,10 +484,12 @@ export class NordUser {
478
484
  amount,
479
485
  tokenId,
480
486
  recipient,
487
+ sendOptions,
481
488
  }: Readonly<{
482
489
  amount: number;
483
490
  tokenId: number;
484
491
  recipient?: PublicKey;
492
+ sendOptions?: SendOptions;
485
493
  }>): Promise<string> {
486
494
  try {
487
495
  // Find the token info
@@ -502,8 +510,7 @@ export class NordUser {
502
510
  sourceTokenAccount: fromAccount,
503
511
  });
504
512
 
505
- const { blockhash } =
506
- await this.connection.getLatestBlockhash("confirmed");
513
+ const { blockhash } = await this.connection.getLatestBlockhash();
507
514
  const tx = new Transaction();
508
515
 
509
516
  tx.add(ix);
@@ -515,6 +522,7 @@ export class NordUser {
515
522
 
516
523
  const signature = await this.connection.sendRawTransaction(
517
524
  signedTx.serialize(),
525
+ sendOptions,
518
526
  );
519
527
 
520
528
  return signature;
@@ -657,7 +665,7 @@ export class NordUser {
657
665
  * @throws {NordError} If the operation fails
658
666
  */
659
667
  async refreshSession(): Promise<void> {
660
- this.sessionId = await createSession(
668
+ const result = await createSession(
661
669
  this.nord.webServerUrl,
662
670
  this.walletSignFn,
663
671
  await this.nord.getTimestamp(),
@@ -667,6 +675,7 @@ export class NordUser {
667
675
  sessionPubkey: this.sessionPubKey,
668
676
  },
669
677
  );
678
+ this.sessionId = result.sessionId;
670
679
  }
671
680
  /**
672
681
  * Revoke a session
@@ -746,10 +755,14 @@ export class NordUser {
746
755
  * Place an order on the exchange
747
756
  *
748
757
  * @param params - Order parameters
749
- * @returns Order ID if successful
758
+ * @returns Object containing actionId, orderId (if posted), fills, and clientOrderId
750
759
  * @throws {NordError} If the operation fails
751
760
  */
752
- async placeOrder(params: PlaceOrderParams): Promise<bigint | undefined> {
761
+ async placeOrder(params: PlaceOrderParams): Promise<{
762
+ actionId: bigint;
763
+ orderId?: bigint;
764
+ fills: proto.Receipt_Trade[];
765
+ }> {
753
766
  try {
754
767
  this.checkSessionValidity();
755
768
  const market = findMarket(this.nord.markets, params.marketId);
@@ -757,7 +770,7 @@ export class NordUser {
757
770
  throw new NordError(`Market with ID ${params.marketId} not found`);
758
771
  }
759
772
 
760
- return placeOrder(
773
+ const result = await placeOrder(
761
774
  this.nord.webServerUrl,
762
775
  this.sessionSignFn,
763
776
  await this.nord.getTimestamp(),
@@ -776,6 +789,7 @@ export class NordUser {
776
789
  quoteSize: params.quoteSize,
777
790
  },
778
791
  );
792
+ return result;
779
793
  } catch (error) {
780
794
  throw new NordError("Failed to place order", { cause: error });
781
795
  }
@@ -786,18 +800,22 @@ export class NordUser {
786
800
  *
787
801
  * @param orderId - Order ID to cancel
788
802
  * @param providedAccountId - Account ID that placed the order
789
- * @returns Action ID if successful
803
+ * @returns Object containing actionId, cancelled orderId, and accountId
790
804
  * @throws {NordError} If the operation fails
791
805
  */
792
806
  async cancelOrder(
793
807
  orderId: BigIntValue,
794
808
  providedAccountId?: number,
795
- ): Promise<bigint> {
809
+ ): Promise<{
810
+ actionId: bigint;
811
+ orderId: bigint;
812
+ accountId: number;
813
+ }> {
796
814
  const accountId =
797
815
  providedAccountId != null ? providedAccountId : this.accountIds?.[0];
798
816
  try {
799
817
  this.checkSessionValidity();
800
- return cancelOrder(
818
+ const result = await cancelOrder(
801
819
  this.nord.webServerUrl,
802
820
  this.sessionSignFn,
803
821
  await this.nord.getTimestamp(),
@@ -808,6 +826,7 @@ export class NordUser {
808
826
  orderId,
809
827
  },
810
828
  );
829
+ return result;
811
830
  } catch (error) {
812
831
  throw new NordError(`Failed to cancel order ${orderId}`, {
813
832
  cause: error,
@@ -860,7 +879,10 @@ export class NordUser {
860
879
  async atomic(
861
880
  userActions: UserAtomicSubaction[],
862
881
  providedAccountId?: number,
863
- ): Promise<proto.Receipt_AtomicResult> {
882
+ ): Promise<{
883
+ actionId: bigint;
884
+ results: proto.Receipt_AtomicSubactionResultKind[];
885
+ }> {
864
886
  try {
865
887
  this.checkSessionValidity();
866
888
 
@@ -889,8 +911,7 @@ export class NordUser {
889
911
  priceDecimals: market.priceDecimals,
890
912
  size: act.size,
891
913
  price: act.price,
892
- quoteSizeSize: act.quoteSize, // treated as quote size; we pass only size component
893
- quoteSizePrice: undefined,
914
+ quoteSize: act.quoteSize,
894
915
  clientOrderId: act.clientOrderId,
895
916
  } as ApiAtomicSubaction;
896
917
  }
package/src/types.ts CHANGED
@@ -1,5 +1,7 @@
1
- import * as proto from "./gen/nord";
1
+ import * as proto from "./gen/nord_pb";
2
2
  import type { components } from "./gen/openapi.ts";
3
+ import Decimal from "decimal.js";
4
+ import { toScaledU64 } from "./utils";
3
5
 
4
6
  /**
5
7
  * The peak TPS rate is queried over the specified period.
@@ -300,3 +302,32 @@ export interface SPLTokenInfo {
300
302
  tokenId: number;
301
303
  name: string;
302
304
  }
305
+
306
+ // Positive decimal price and size.
307
+ export class QuoteSize {
308
+ price: Decimal;
309
+ size: Decimal;
310
+ constructor(quotePrice: Decimal.Value, quoteSize: Decimal.Value) {
311
+ const p = new Decimal(quotePrice);
312
+ const s = new Decimal(quoteSize);
313
+ if (p.isZero() || s.isZero()) {
314
+ throw new Error("quotePrice and quoteSize must be non-zero");
315
+ }
316
+ this.price = p;
317
+ this.size = s;
318
+ }
319
+
320
+ value(): Decimal {
321
+ return this.price.mul(this.size);
322
+ }
323
+
324
+ toScaledU64(
325
+ marketPriceDecimals: number,
326
+ marketSizeDecimals: number,
327
+ ): { price: bigint; size: bigint } {
328
+ return {
329
+ price: toScaledU64(this.price, marketPriceDecimals),
330
+ size: toScaledU64(this.size, marketSizeDecimals),
331
+ };
332
+ }
333
+ }
package/src/utils.ts CHANGED
@@ -4,8 +4,9 @@ import { bls12_381 as bls } from "@noble/curves/bls12-381";
4
4
  import { secp256k1 as secp } from "@noble/curves/secp256k1";
5
5
  import { sha256 } from "@noble/hashes/sha256";
6
6
  import { KeyType, type Market, type Token } from "./types";
7
- import * as proto from "./gen/nord";
8
- import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
7
+ import { sizeDelimitedPeek } from "@bufbuild/protobuf/wire";
8
+ import { fromBinary, type Message } from "@bufbuild/protobuf";
9
+ import type { GenMessage } from "@bufbuild/protobuf/codegenv2";
9
10
  import { ethers } from "ethers";
10
11
  import fetch from "node-fetch";
11
12
  import { RequestInfo, RequestInit, Response } from "node-fetch";
@@ -101,6 +102,7 @@ export function makeWalletSignFn(
101
102
  signingKey.sign(ethers.hashMessage(message)).serialized;
102
103
  }
103
104
 
105
+ // Returned numbers do fit into specified bits range, or error is thrown.
104
106
  function makeToScaledBigUint(params: {
105
107
  precision: number;
106
108
  exponent: number;
@@ -176,63 +178,42 @@ export const toScaledU128 = makeToScaledBigUint({
176
178
  exponent: 56,
177
179
  });
178
180
 
179
- /**
180
- * Encodes any protobuf message into a length-delimited format,
181
- * i.e. prefixed with its length encoded as varint
182
- * @param message message object
183
- * @param coder associated coder object which implements `MessageFns` interface
184
- * @returns Encoded message as Uint8Array, prefixed with its length
185
- */
186
- export function encodeLengthDelimited<T, M extends proto.MessageFns<T>>(
187
- message: T,
188
- coder: M,
189
- ): Uint8Array {
190
- const encoded = coder.encode(message).finish();
191
- if (encoded.byteLength > MAX_PAYLOAD_SIZE) {
192
- throw new Error(
193
- `Encoded message size (${encoded.byteLength} bytes) is greater than max payload size (${MAX_PAYLOAD_SIZE} bytes).`,
194
- );
195
- }
196
- const encodedLength = new BinaryWriter().uint32(encoded.byteLength).finish();
197
- return new Uint8Array([...encodedLength, ...encoded]);
198
- }
199
-
200
181
  /**
201
182
  * Decodes any protobuf message from a length-delimited format,
202
183
  * i.e. prefixed with its length encoded as varint
203
184
  *
204
- * NB: Please note that due to limitations of Typescript type inference
205
- * it requires to specify variable type explicitly:
206
- *
207
- * ```
208
- * const foo: proto.Bar = decodeLengthDelimited(bytes, proto.Bar);
209
- * ```
210
- *
211
- * @param bytes Byte array with encoded message
212
- * @param coder associated coder object which implements `MessageFns` interface
213
- * @returns Decoded Action as Uint8Array.
185
+ * @param bytes Byte array with encoded message
186
+ * @param schema Message schema for decoding
187
+ * @returns Decoded message
214
188
  */
215
- export function decodeLengthDelimited<T, M extends proto.MessageFns<T>>(
189
+ export function decodeLengthDelimited<T extends Message>(
216
190
  bytes: Uint8Array,
217
- coder: M,
191
+ schema: GenMessage<T>,
218
192
  ): T {
219
- const lengthReader = new BinaryReader(bytes);
220
- const msgLength = lengthReader.uint32();
221
- const startsAt = lengthReader.pos;
193
+ // use sizeDelimitedPeek to extract the message length and offset
194
+ const peekResult = sizeDelimitedPeek(bytes);
195
+
196
+ if (peekResult.size === null || peekResult.offset === null) {
197
+ throw new Error("Failed to parse size-delimited message");
198
+ }
222
199
 
223
- if (msgLength > MAX_PAYLOAD_SIZE) {
200
+ if (peekResult.size > MAX_PAYLOAD_SIZE) {
224
201
  throw new Error(
225
- `Encoded message size (${msgLength} bytes) is greater than max payload size (${MAX_PAYLOAD_SIZE} bytes).`,
202
+ `Encoded message size (${peekResult.size} bytes) is greater than max payload size (${MAX_PAYLOAD_SIZE} bytes).`,
226
203
  );
227
204
  }
228
205
 
229
- if (startsAt + msgLength > bytes.byteLength) {
206
+ if (peekResult.offset + peekResult.size > bytes.length) {
230
207
  throw new Error(
231
- `Encoded message size (${msgLength} bytes) is greater than remaining buffer size (${bytes.byteLength - startsAt} bytes).`,
208
+ `Encoded message size (${peekResult.size} bytes) is greater than remaining buffer size (${bytes.length - peekResult.offset} bytes).`,
232
209
  );
233
210
  }
234
211
 
235
- return coder.decode(bytes.slice(startsAt, startsAt + msgLength));
212
+ // decode the message using the offset and size from peek
213
+ return fromBinary(
214
+ schema,
215
+ bytes.slice(peekResult.offset, peekResult.offset + peekResult.size),
216
+ );
236
217
  }
237
218
 
238
219
  export function checkPubKeyLength(keyType: KeyType, len: number): void {
@@ -27,9 +27,6 @@ type WebSocketInstance = WebSocket | BrowserWebSocket;
27
27
 
28
28
  const VALID_STREAM_TYPES = ["trades", "delta", "account"];
29
29
 
30
- // Constants for WebSocket readyState
31
- const WS_OPEN = 1;
32
-
33
30
  /**
34
31
  * WebSocket client for Nord exchange
35
32
  *
@@ -189,10 +186,7 @@ export class NordWebSocketClient
189
186
  }
190
187
  };
191
188
 
192
- (this.ws as BrowserWebSocket).onclose = (event: any) => {
193
- const reason =
194
- event && event.reason ? ` Reason: ${event.reason}` : "";
195
- const code = event && event.code ? ` Code: ${event.code}` : "";
189
+ (this.ws as BrowserWebSocket).onclose = (_event: any) => {
196
190
  this.emit("disconnected");
197
191
  this.reconnect();
198
192
  };
@@ -227,7 +221,7 @@ export class NordWebSocketClient
227
221
  }
228
222
  });
229
223
 
230
- nodeWs.on("close", (code: number, reason: string) => {
224
+ nodeWs.on("close", (_code: number, _reason: string) => {
231
225
  this.emit("disconnected");
232
226
  if (this.pingInterval) {
233
227
  clearInterval(this.pingInterval);