@n1xyz/nord-ts 0.3.5 → 0.3.6

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/README.md +21 -0
  2. package/dist/actions.d.ts +2 -2
  3. package/dist/actions.js +184 -0
  4. package/dist/client/Nord.js +759 -0
  5. package/dist/client/NordAdmin.js +362 -0
  6. package/dist/client/NordUser.d.ts +39 -9
  7. package/dist/client/NordUser.js +752 -0
  8. package/dist/const.js +27 -0
  9. package/dist/error.js +51 -0
  10. package/dist/gen/nord_pb.d.ts +772 -171
  11. package/dist/gen/nord_pb.js +1068 -0
  12. package/dist/gen/openapi.d.ts +46 -4
  13. package/dist/gen/openapi.js +5 -0
  14. package/dist/index.browser.js +63581 -82745
  15. package/dist/index.common.js +60281 -88068
  16. package/dist/index.js +10 -0
  17. package/dist/nord/api/actions.d.ts +128 -0
  18. package/dist/nord/api/actions.js +396 -0
  19. package/dist/nord/api/core.d.ts +16 -0
  20. package/dist/nord/api/core.js +81 -0
  21. package/dist/nord/api/metrics.d.ts +67 -0
  22. package/dist/nord/api/metrics.js +229 -0
  23. package/dist/nord/api/triggers.d.ts +7 -0
  24. package/dist/nord/api/triggers.js +38 -0
  25. package/dist/nord/client/Nord.d.ts +387 -0
  26. package/dist/nord/client/Nord.js +747 -0
  27. package/dist/nord/client/NordAdmin.d.ts +226 -0
  28. package/dist/nord/client/NordAdmin.js +410 -0
  29. package/dist/nord/client/NordClient.d.ts +16 -0
  30. package/dist/nord/client/NordClient.js +28 -0
  31. package/dist/nord/client/NordUser.d.ts +379 -0
  32. package/dist/nord/client/NordUser.js +787 -0
  33. package/dist/nord/index.d.ts +8 -0
  34. package/dist/nord/index.js +34 -0
  35. package/dist/nord/models/Subscriber.d.ts +37 -0
  36. package/dist/nord/models/Subscriber.js +25 -0
  37. package/dist/nord/utils/NordError.d.ts +35 -0
  38. package/dist/nord/utils/NordError.js +49 -0
  39. package/dist/types.d.ts +1 -12
  40. package/dist/types.js +92 -0
  41. package/dist/utils.d.ts +4 -0
  42. package/dist/utils.js +193 -0
  43. package/dist/websocket/NordWebSocketClient.d.ts +3 -0
  44. package/dist/websocket/NordWebSocketClient.js +242 -0
  45. package/dist/websocket/Subscriber.js +24 -0
  46. package/dist/websocket/events.js +1 -0
  47. package/dist/websocket/index.js +80 -0
  48. package/package.json +1 -1
package/README.md CHANGED
@@ -110,6 +110,27 @@ try {
110
110
  }
111
111
  ```
112
112
 
113
+ ### Quote-Sized Orders
114
+
115
+ For orders limited by quote amount (e.g., USD value), pass a single decimal value:
116
+
117
+ ```typescript
118
+ // Place a market/limit order capped by quote value
119
+ await user.placeOrder({
120
+ marketId: 0,
121
+ side: Side.Bid,
122
+ fillMode: FillMode.Limit,
123
+ isReduceOnly: false,
124
+ // Limit by quote: $100 at $50,000 results in ~0.002 BTC
125
+ quoteSize: 100,
126
+ });
127
+ ```
128
+
129
+ Notes:
130
+ - `quoteSize` is a positive decimal representing the desired quote amount.
131
+ - The wire format encodes quote amount as a 128-bit value scaled by `price_decimals + size_decimals` of the target market.
132
+ - At least one limit must be provided among `size`, `price`, or `quoteSize`.
133
+
113
134
  ### Deposits and Withdrawals
114
135
 
115
136
  ```typescript
package/dist/actions.d.ts CHANGED
@@ -2,7 +2,7 @@ import Decimal from "decimal.js";
2
2
  import * as proto from "./gen/nord_pb";
3
3
  import { paths } from "./gen/openapi";
4
4
  import { Client } from "openapi-fetch";
5
- import { FillMode, Side, QuoteSize } from "./types";
5
+ import { FillMode, Side } from "./types";
6
6
  import { BigIntValue } from "./utils";
7
7
  import { PublicKey, Transaction } from "@solana/web3.js";
8
8
  type ReceiptKind = NonNullable<proto.Receipt["kind"]>;
@@ -41,7 +41,7 @@ export type AtomicSubaction = {
41
41
  priceDecimals: number;
42
42
  size?: Decimal.Value;
43
43
  price?: Decimal.Value;
44
- quoteSize?: QuoteSize;
44
+ quoteSize?: Decimal.Value;
45
45
  clientOrderId?: BigIntValue;
46
46
  } | {
47
47
  kind: "cancel";
@@ -0,0 +1,184 @@
1
+ import * as proto from "./gen/nord_pb";
2
+ import { create } from "@bufbuild/protobuf";
3
+ import { fillModeToProtoFillMode, Side } from "./types";
4
+ import { assert, decodeLengthDelimited, SESSION_TTL, toScaledU64, signUserPayload, } from "./utils";
5
+ import { sizeDelimitedEncode } from "@bufbuild/protobuf/wire";
6
+ import { NordError } from "./error";
7
+ export function formatReceiptError(receipt) {
8
+ if (receipt.kind?.case === "err") {
9
+ const err = receipt.kind.value;
10
+ return proto.Error[err] ?? err.toString();
11
+ }
12
+ return receipt.kind?.case ?? "unknown";
13
+ }
14
+ export function expectReceiptKind(receipt, expected, action) {
15
+ if (receipt.kind?.case !== expected) {
16
+ const label = formatReceiptError(receipt);
17
+ throw new NordError(`Failed to ${action}: ${label}`);
18
+ }
19
+ }
20
+ async function sessionSign(signFn, message) {
21
+ const signature = await signFn(message);
22
+ return new Uint8Array([...message, ...signature]);
23
+ }
24
+ // Helper to create an action with common fields
25
+ export function createAction(currentTimestamp, nonce, kind) {
26
+ return create(proto.ActionSchema, {
27
+ currentTimestamp,
28
+ nonce,
29
+ kind,
30
+ });
31
+ }
32
+ export async function sendAction(client, makeSignedMessage, action) {
33
+ const body = await prepareAction(action, makeSignedMessage);
34
+ const response = await client.POST("/action", {
35
+ params: {
36
+ header: {
37
+ "content-type": "application/octet-stream",
38
+ },
39
+ },
40
+ body: body,
41
+ // NOTE: openapi-fetch ignores headers and types/const headers in schema, and always assume all things are JSON
42
+ // to handle multi type bodies, need these overrides and later adhoc parsing
43
+ bodySerializer: (body) => body,
44
+ parseAs: "stream",
45
+ });
46
+ if (response.error) {
47
+ throw new Error(`Failed to ${action.kind.case}, HTTP status ${JSON.stringify(response.error)}`);
48
+ }
49
+ const rawResp = new Uint8Array(await response.response.arrayBuffer());
50
+ const resp = decodeLengthDelimited(rawResp, proto.ReceiptSchema);
51
+ if (resp.kind?.case === "err") {
52
+ throw new Error(`Could not execute ${action.kind.case}, reason: ${proto.Error[resp.kind.value]}`);
53
+ }
54
+ return resp;
55
+ }
56
+ // Given action and signature function, prepare the signed message to send to server as `body`.
57
+ // `makeSignedMessage` must include the original message and signature.
58
+ export async function prepareAction(action, makeSignedMessage) {
59
+ const encoded = sizeDelimitedEncode(proto.ActionSchema, action);
60
+ // NOTE(agent): keep in sync with MAX_ENCODED_ACTION_SIZE in Rust code
61
+ const MAX_ENCODED_ACTION_SIZE = 1024;
62
+ if (encoded.byteLength > MAX_ENCODED_ACTION_SIZE) {
63
+ console.warn("Encoded message:", encoded);
64
+ throw new Error(`Encoded message size (${encoded.byteLength} bytes) is greater than max payload size (${MAX_ENCODED_ACTION_SIZE} bytes).`);
65
+ }
66
+ const body = await makeSignedMessage(encoded);
67
+ if (body.byteLength > MAX_ENCODED_ACTION_SIZE) {
68
+ console.warn("Encoded length:", encoded.byteLength);
69
+ throw new Error(`Signed message size (${body.byteLength} bytes) is greater than max payload size (${MAX_ENCODED_ACTION_SIZE} bytes).`);
70
+ }
71
+ return body;
72
+ }
73
+ export async function createSession(client, signMessage, currentTimestamp, nonce, params) {
74
+ let expiry = 0n;
75
+ if (params.expiryTimestamp !== undefined) {
76
+ expiry = params.expiryTimestamp;
77
+ assert(expiry > currentTimestamp, "Cannot set expiry timestamp in the past");
78
+ }
79
+ else {
80
+ expiry = currentTimestamp + SESSION_TTL;
81
+ }
82
+ const action = createAction(currentTimestamp, nonce, {
83
+ case: "createSession",
84
+ value: create(proto.Action_CreateSessionSchema, {
85
+ userPubkey: params.userPubkey.toBytes(),
86
+ blstPubkey: params.sessionPubkey.toBytes(),
87
+ expiryTimestamp: expiry,
88
+ }),
89
+ });
90
+ const resp = await sendAction(client, async (payload) => {
91
+ return new Uint8Array([
92
+ ...payload,
93
+ ...(await signUserPayload({
94
+ payload,
95
+ signMessage,
96
+ })),
97
+ ]);
98
+ }, action);
99
+ if (resp.kind?.case === "createSessionResult") {
100
+ return {
101
+ actionId: resp.actionId,
102
+ sessionId: resp.kind.value.sessionId,
103
+ };
104
+ }
105
+ else {
106
+ throw new Error(`Unexpected receipt kind ${resp.kind?.case}`);
107
+ }
108
+ }
109
+ export async function revokeSession(client, signMessage, currentTimestamp, nonce, params) {
110
+ const action = createAction(currentTimestamp, nonce, {
111
+ case: "revokeSession",
112
+ value: create(proto.Action_RevokeSessionSchema, {
113
+ sessionId: BigInt(params.sessionId),
114
+ }),
115
+ });
116
+ const resp = await sendAction(client, async (payload) => {
117
+ return new Uint8Array([
118
+ ...payload,
119
+ ...(await signUserPayload({
120
+ payload,
121
+ signMessage,
122
+ })),
123
+ ]);
124
+ }, action);
125
+ return { actionId: resp.actionId };
126
+ }
127
+ export async function atomic(client, signFn, currentTimestamp, nonce, params) {
128
+ assert(params.actions.length > 0 && params.actions.length <= 4, "Atomic action must contain between 1 and 4 sub-actions");
129
+ const subactions = params.actions.map((a) => {
130
+ if (a.kind === "place") {
131
+ const price = toScaledU64(a.price ?? 0, a.priceDecimals);
132
+ const size = toScaledU64(a.size ?? 0, a.sizeDecimals);
133
+ const scaledQuote = a.quoteSize
134
+ ? a.quoteSize.toWire(a.priceDecimals, a.sizeDecimals)
135
+ : undefined;
136
+ // Require at least one limit to be set (non-zero size, non-zero price, or quoteSize)
137
+ assert(price > 0n || size > 0n || scaledQuote !== undefined, "OrderLimit must include at least one of: size, price, or quoteSize");
138
+ const tradeOrPlace = create(proto.TradeOrPlaceSchema, {
139
+ marketId: a.marketId,
140
+ orderType: create(proto.OrderTypeSchema, {
141
+ side: a.side === Side.Bid ? proto.Side.BID : proto.Side.ASK,
142
+ fillMode: fillModeToProtoFillMode(a.fillMode),
143
+ isReduceOnly: a.isReduceOnly,
144
+ }),
145
+ limit: create(proto.OrderLimitSchema, {
146
+ price,
147
+ size,
148
+ quoteSize: scaledQuote === undefined
149
+ ? undefined
150
+ : create(proto.QuoteSizeSchema, {
151
+ size: scaledQuote.size,
152
+ price: scaledQuote.price,
153
+ }),
154
+ }),
155
+ clientOrderId: a.clientOrderId === undefined ? undefined : BigInt(a.clientOrderId),
156
+ });
157
+ return create(proto.AtomicSubactionKindSchema, {
158
+ inner: { case: "tradeOrPlace", value: tradeOrPlace },
159
+ });
160
+ }
161
+ return create(proto.AtomicSubactionKindSchema, {
162
+ inner: {
163
+ case: "cancelOrder",
164
+ value: create(proto.CancelOrderSchema, { orderId: BigInt(a.orderId) }),
165
+ },
166
+ });
167
+ });
168
+ const action = createAction(currentTimestamp, nonce, {
169
+ case: "atomic",
170
+ value: create(proto.AtomicSchema, {
171
+ sessionId: BigInt(params.sessionId),
172
+ accountId: params.accountId, // optional
173
+ actions: subactions,
174
+ }),
175
+ });
176
+ const resp = await sendAction(client, (m) => sessionSign(signFn, m), action);
177
+ if (resp.kind?.case === "atomic") {
178
+ return {
179
+ actionId: resp.actionId,
180
+ results: resp.kind.value.results,
181
+ };
182
+ }
183
+ throw new Error(`Unexpected receipt kind ${resp.kind?.case}`);
184
+ }