@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
@@ -32,17 +32,21 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
35
38
  Object.defineProperty(exports, "__esModule", { value: true });
36
39
  exports.Nord = void 0;
37
40
  const events_1 = require("events");
41
+ const web3_js_1 = require("@solana/web3.js");
42
+ const openapi_fetch_1 = __importDefault(require("openapi-fetch"));
38
43
  const types_1 = require("../../types");
39
44
  const proton_1 = require("@n1xyz/proton");
45
+ const proto = __importStar(require("../../gen/nord"));
40
46
  const core = __importStar(require("../api/core"));
41
- const market = __importStar(require("../api/market"));
42
47
  const metrics = __importStar(require("../api/metrics"));
43
- const queries = __importStar(require("../api/queries"));
48
+ const utils = __importStar(require("../../utils"));
44
49
  const NordError_1 = require("../utils/NordError");
45
- const web3_js_1 = require("@solana/web3.js");
46
50
  /**
47
51
  * Main Nord client class for interacting with the Nord API
48
52
  */
@@ -67,6 +71,7 @@ class Nord {
67
71
  this.bridgeVk = bridgeVk;
68
72
  this.solanaUrl = solanaUrl;
69
73
  this.protonClient = protonClient;
74
+ this.httpClient = (0, openapi_fetch_1.default)({ baseUrl: webServerUrl });
70
75
  }
71
76
  /**
72
77
  * Create a WebSocket client with specific subscriptions
@@ -119,6 +124,18 @@ class Nord {
119
124
  // Create and return a new WebSocket client
120
125
  return core.initWebSocketClient(this.webServerUrl, subscriptions);
121
126
  }
127
+ async GET(path, options) {
128
+ const r = await this.httpClient.GET(path, options);
129
+ if (r.error) {
130
+ throw new NordError_1.NordError(`failed to GET ${path}`, { cause: r.error });
131
+ }
132
+ if (r.data === undefined) {
133
+ // this should never happen, but the type checker seems unhappy.
134
+ // if we catch this we'll need to debug accordingly.
135
+ throw new NordError_1.NordError("internal assertion violation", { cause: r });
136
+ }
137
+ return r.data;
138
+ }
122
139
  /**
123
140
  * Get the current timestamp from the Nord server
124
141
  *
@@ -126,7 +143,7 @@ class Nord {
126
143
  * @throws {NordError} If the request fails
127
144
  */
128
145
  async getTimestamp() {
129
- return core.getTimestamp(this.webServerUrl);
146
+ return BigInt(await this.GET("/timestamp", {}));
130
147
  }
131
148
  /**
132
149
  * Get the last event nonce from the Nord server
@@ -135,7 +152,7 @@ class Nord {
135
152
  * @throws {NordError} If the request fails
136
153
  */
137
154
  async getActionNonce() {
138
- return core.getLastEventNonce(this.webServerUrl);
155
+ return await this.GET("/event/last-acked-nonce", {});
139
156
  }
140
157
  /**
141
158
  * Fetch information about Nord markets and tokens
@@ -144,7 +161,7 @@ class Nord {
144
161
  */
145
162
  async fetchNordInfo() {
146
163
  try {
147
- const info = await core.getInfo(this.webServerUrl);
164
+ const info = await this.GET("/info", {});
148
165
  this.markets = info.markets;
149
166
  this.tokens = info.tokens;
150
167
  // Populate the symbolToMarketId map
@@ -194,15 +211,6 @@ class Nord {
194
211
  async init() {
195
212
  await this.fetchNordInfo();
196
213
  }
197
- /**
198
- * Get market statistics
199
- *
200
- * @returns Market statistics response
201
- * @throws {NordError} If the request fails
202
- */
203
- async marketsStats() {
204
- return market.marketsStats(this.webServerUrl);
205
- }
206
214
  /**
207
215
  * Query a specific action
208
216
  *
@@ -210,8 +218,11 @@ class Nord {
210
218
  * @returns Action response
211
219
  * @throws {NordError} If the request fails
212
220
  */
213
- async queryAction(query) {
214
- return queries.queryAction(this.webServerUrl, query);
221
+ async queryAction({ action_id, }) {
222
+ return ((await this.queryRecentActions({
223
+ from: action_id,
224
+ to: action_id,
225
+ }))[0] ?? null);
215
226
  }
216
227
  /**
217
228
  * Query recent actions
@@ -221,8 +232,17 @@ class Nord {
221
232
  * @returns Actions response
222
233
  * @throws {NordError} If the request fails
223
234
  */
224
- async queryRecentActions(from, to) {
225
- return queries.queryRecentActions(this.webServerUrl, from, to);
235
+ async queryRecentActions(query) {
236
+ const xs = await this.GET("/action", {
237
+ params: {
238
+ query,
239
+ },
240
+ });
241
+ return xs.map((x) => ({
242
+ actionId: x.actionId,
243
+ action: utils.decodeLengthDelimited(Buffer.from(x.payload, "base64"), proto.Action),
244
+ physicalExecTime: new Date(x.physicalTime * 1000),
245
+ }));
226
246
  }
227
247
  /**
228
248
  * Get the last action ID
@@ -231,7 +251,7 @@ class Nord {
231
251
  * @throws {NordError} If the request fails
232
252
  */
233
253
  async getLastActionId() {
234
- return queries.getLastActionId(this.webServerUrl);
254
+ return await this.GET("/action/last-executed-id", {});
235
255
  }
236
256
  /**
237
257
  * Fetch aggregate metrics from the Nord API
@@ -283,26 +303,6 @@ class Nord {
283
303
  async getTotalTransactions() {
284
304
  return metrics.getTotalTransactions(this.webServerUrl);
285
305
  }
286
- /**
287
- * Query an action from Rollman
288
- *
289
- * @param query - Action query parameters
290
- * @returns Rollman action response
291
- * @throws {NordError} If the request fails
292
- */
293
- async actionQueryRollman(query) {
294
- return queries.actionQueryRollman(this.webServerUrl, query);
295
- }
296
- /**
297
- * Query actions from Rollman
298
- *
299
- * @param last_n - Number of recent actions to query
300
- * @returns Rollman actions response
301
- * @throws {NordError} If the request fails
302
- */
303
- async actionsQueryRollman(last_n) {
304
- return queries.actionsQueryRollman(this.webServerUrl, last_n);
305
- }
306
306
  /**
307
307
  * Query Prometheus metrics
308
308
  *
@@ -408,7 +408,26 @@ class Nord {
408
408
  * @throws {NordError} If the request fails
409
409
  */
410
410
  async getTrades(query) {
411
- return market.getTrades(this.webServerUrl, query);
411
+ if (query.sinceRcf3339 && !utils.isRfc3339(query.sinceRcf3339)) {
412
+ throw new NordError_1.NordError(`Invalid RFC3339 timestamp: ${query.sinceRcf3339}`);
413
+ }
414
+ if (query.untilRfc3339 && !utils.isRfc3339(query.untilRfc3339)) {
415
+ throw new NordError_1.NordError(`Invalid RFC3339 timestamp: ${query.untilRfc3339}`);
416
+ }
417
+ return await this.GET("/trades", {
418
+ params: {
419
+ query: {
420
+ takerId: query.takerId,
421
+ makerId: query.makerId,
422
+ marketId: query.marketId,
423
+ pageSize: query.pageSize,
424
+ takerSide: query.takerSide,
425
+ since: query.sinceRcf3339,
426
+ until: query.untilRfc3339,
427
+ startInclusive: query.pageId,
428
+ },
429
+ },
430
+ });
412
431
  }
413
432
  /**
414
433
  * Get user account IDs
@@ -417,8 +436,16 @@ class Nord {
417
436
  * @returns User account IDs response
418
437
  * @throws {NordError} If the request fails
419
438
  */
420
- async getUserAccountIds(query) {
421
- return market.getUserAccountIds(this.webServerUrl, query);
439
+ async getUser(query) {
440
+ const r = await this.httpClient.GET("/user/{pubkey}", {
441
+ params: {
442
+ path: { pubkey: query.pubkey.toString() },
443
+ },
444
+ });
445
+ if (r.response.status === 404) {
446
+ return null;
447
+ }
448
+ return r.data;
422
449
  }
423
450
  /**
424
451
  * Get orderbook for a market
@@ -431,18 +458,29 @@ class Nord {
431
458
  */
432
459
  async getOrderbook(query) {
433
460
  // If only symbol is provided, convert it to market_id
461
+ let marketId;
434
462
  if (query.symbol && query.market_id === undefined) {
435
463
  // If the map is empty, try to fetch market information first
436
464
  if (this.symbolToMarketId.size === 0) {
437
465
  await this.fetchNordInfo();
438
466
  }
439
- const marketId = this.symbolToMarketId.get(query.symbol);
440
- if (marketId === undefined) {
467
+ const id = this.symbolToMarketId.get(query.symbol);
468
+ if (id === undefined) {
441
469
  throw new NordError_1.NordError(`Unknown market symbol: ${query.symbol}`);
442
470
  }
443
- query = { market_id: marketId };
471
+ marketId = id;
472
+ }
473
+ else if (query.market_id !== undefined) {
474
+ marketId = query.market_id;
475
+ }
476
+ else {
477
+ throw new NordError_1.NordError("Either symbol or market_id must be provided for orderbook query");
444
478
  }
445
- return market.getOrderbook(this.webServerUrl, query);
479
+ return await this.GET("/market/{market_id}/orderbook", {
480
+ params: {
481
+ path: { market_id: marketId },
482
+ },
483
+ });
446
484
  }
447
485
  /**
448
486
  * Get information about the Nord server
@@ -451,7 +489,7 @@ class Nord {
451
489
  * @throws {NordError} If the request fails
452
490
  */
453
491
  async getInfo() {
454
- return core.getInfo(this.webServerUrl);
492
+ return await this.GET("/info", {});
455
493
  }
456
494
  /**
457
495
  * Get account information
@@ -461,31 +499,33 @@ class Nord {
461
499
  * @throws {NordError} If the request fails
462
500
  */
463
501
  async getAccount(accountId) {
464
- return core.getAccount(this.webServerUrl, accountId);
502
+ return await this.GET("/account/{account_id}", {
503
+ params: {
504
+ path: { account_id: accountId },
505
+ },
506
+ });
465
507
  }
466
508
  /**
467
509
  * Get market statistics (alias for marketsStats for backward compatibility)
468
510
  *
469
- * @deprecated Use marketsStats instead
470
511
  * @returns Market statistics response
471
512
  */
472
- async getMarketStats() {
473
- return this.marketsStats();
513
+ async getMarketStats({ marketId, }) {
514
+ return await this.GET("/market/{market_id}/stats", {
515
+ params: {
516
+ path: { market_id: marketId },
517
+ },
518
+ });
474
519
  }
475
520
  /**
476
521
  * Check if an account exists for the given address
477
522
  *
478
523
  * @param address - The public key address to check
479
524
  * @returns True if the account exists, false otherwise
525
+ * @deprecated use getUser instead
480
526
  */
481
- async accountExists(address) {
482
- try {
483
- await market.getUserAccountIds(this.webServerUrl, { pubkey: address });
484
- return true;
485
- }
486
- catch {
487
- return false;
488
- }
527
+ async accountExists(pubkey) {
528
+ return !!(await this.getUser({ pubkey }));
489
529
  }
490
530
  }
491
531
  exports.Nord = Nord;
@@ -1,7 +1,7 @@
1
- import { Connection, PublicKey } from "@solana/web3.js";
1
+ import { Connection, PublicKey, Transaction } from "@solana/web3.js";
2
2
  import Decimal from "decimal.js";
3
- import { SPLTokenInfo } from "@n1xyz/proton";
4
- import { FillMode, Order, Side } from "../../types";
3
+ import { FillMode, Side, SPLTokenInfo } from "../../types";
4
+ import * as proto from "../../gen/nord";
5
5
  import { BigIntValue } from "../../utils";
6
6
  import { Nord } from "./Nord";
7
7
  /**
@@ -17,7 +17,7 @@ export interface NordUserParams {
17
17
  /** Function to sign messages with the user's session key */
18
18
  sessionSignFn: (message: Uint8Array) => Promise<Uint8Array>;
19
19
  /** Function to sign transactions with the user's wallet (optional) */
20
- transactionSignFn: (transaction: any) => Promise<any>;
20
+ transactionSignFn: <T extends Transaction>(tx: T) => Promise<T>;
21
21
  /** Solana connection (optional) */
22
22
  connection?: Connection;
23
23
  /** Session ID (optional) */
@@ -63,6 +63,31 @@ export interface TransferParams {
63
63
  /** Destination account ID */
64
64
  toAccountId: number;
65
65
  }
66
+ /**
67
+ * Parameters for individual atomic subactions (user-friendly version)
68
+ */
69
+ export interface UserAtomicSubaction {
70
+ /** The type of action to perform. */
71
+ kind: "place" | "cancel";
72
+ /** The market ID to place the order in. */
73
+ marketId?: number;
74
+ /** The order ID to cancel. */
75
+ orderId?: BigIntValue;
76
+ /** Order side (bid or ask) */
77
+ side?: Side;
78
+ /** Fill mode (limit, market, etc.) */
79
+ fillMode?: FillMode;
80
+ /** Whether the order is reduce-only. */
81
+ isReduceOnly?: boolean;
82
+ /** The size of the order. */
83
+ size?: Decimal.Value;
84
+ /** Order price */
85
+ price?: Decimal.Value;
86
+ /** Quote size (for market orders) */
87
+ quoteSize?: Decimal.Value;
88
+ /** The client order ID of the order. */
89
+ clientOrderId?: BigIntValue;
90
+ }
66
91
  /**
67
92
  * User class for interacting with the Nord protocol
68
93
  */
@@ -76,7 +101,7 @@ export declare class NordUser {
76
101
  /** Function to sign messages with the user's session key */
77
102
  readonly sessionSignFn: (message: Uint8Array) => Promise<Uint8Array>;
78
103
  /** Function to sign transactions with the user's wallet */
79
- readonly transactionSignFn: (transaction: any) => Promise<any>;
104
+ readonly transactionSignFn: <T extends Transaction>(tx: T) => Promise<T>;
80
105
  /** User balances by token symbol */
81
106
  balances: {
82
107
  [key: string]: {
@@ -85,10 +110,6 @@ export declare class NordUser {
85
110
  symbol: string;
86
111
  }[];
87
112
  };
88
- /** User orders by market symbol */
89
- orders: {
90
- [key: string]: Order[];
91
- };
92
113
  /** User positions by account ID */
93
114
  positions: {
94
115
  [key: string]: {
@@ -170,10 +191,26 @@ export declare class NordUser {
170
191
  *
171
192
  * @param amount - Amount to deposit
172
193
  * @param tokenId - Token ID
194
+ * @param recipient - Recipient address; defaults to the user's address
173
195
  * @returns Transaction signature
196
+ * @deprecated Use deposit instead
174
197
  * @throws {NordError} If required parameters are missing or operation fails
175
198
  */
176
- depositSpl(amount: number, tokenId: number): Promise<string>;
199
+ depositSpl(amount: number, tokenId: number, recipient?: PublicKey): Promise<string>;
200
+ /**
201
+ * Deposit SPL tokens to the bridge
202
+ *
203
+ * @param amount - Amount to deposit
204
+ * @param tokenId - Token ID
205
+ * @param recipient - Recipient address; defaults to the user's address
206
+ * @returns Transaction signature
207
+ * @throws {NordError} If required parameters are missing or operation fails
208
+ */
209
+ deposit({ amount, tokenId, recipient, }: Readonly<{
210
+ amount: number;
211
+ tokenId: number;
212
+ recipient?: PublicKey;
213
+ }>): Promise<string>;
177
214
  /**
178
215
  * Get a new nonce for actions
179
216
  *
@@ -218,7 +255,10 @@ export declare class NordUser {
218
255
  * @param amount - Amount to withdraw
219
256
  * @throws {NordError} If the operation fails
220
257
  */
221
- withdraw(tokenId: number, amount: number): Promise<{
258
+ withdraw({ amount, tokenId, }: Readonly<{
259
+ tokenId: number;
260
+ amount: number;
261
+ }>): Promise<{
222
262
  actionId: bigint;
223
263
  }>;
224
264
  /**
@@ -245,6 +285,19 @@ export declare class NordUser {
245
285
  * @throws {NordError} If the operation fails
246
286
  */
247
287
  transferToAccount(params: TransferParams): Promise<void>;
288
+ /**
289
+ * Execute up to four place/cancel operations atomically.
290
+ * Per Market:
291
+ * 1. cancels can only be in the start (one cannot predict future order ids)
292
+ * 2. intermediate trades can trade only
293
+ * 3. placements go last
294
+ *
295
+ * Across Markets, order action can be any
296
+ *
297
+ * @param userActions array of user-friendly subactions
298
+ * @param providedAccountId optional account performing the action (defaults to first account)
299
+ */
300
+ atomic(userActions: UserAtomicSubaction[], providedAccountId?: number): Promise<proto.Receipt_AtomicResult>;
248
301
  /**
249
302
  * Helper function to retry a promise with exponential backoff
250
303
  *
@@ -56,8 +56,6 @@ class NordUser {
56
56
  constructor({ address, nord, publicKey, sessionPubKey, sessionSignFn, transactionSignFn, walletSignFn, connection, sessionId, }) {
57
57
  /** User balances by token symbol */
58
58
  this.balances = {};
59
- /** User orders by market symbol */
60
- this.orders = {};
61
59
  /** User positions by account ID */
62
60
  this.positions = {};
63
61
  /** User margins by account ID */
@@ -126,7 +124,6 @@ class NordUser {
126
124
  });
127
125
  // Copy other properties
128
126
  cloned.balances = { ...this.balances };
129
- cloned.orders = { ...this.orders };
130
127
  cloned.positions = { ...this.positions };
131
128
  cloned.margins = { ...this.margins };
132
129
  cloned.accountIds = this.accountIds ? [...this.accountIds] : undefined;
@@ -231,10 +228,24 @@ class NordUser {
231
228
  *
232
229
  * @param amount - Amount to deposit
233
230
  * @param tokenId - Token ID
231
+ * @param recipient - Recipient address; defaults to the user's address
234
232
  * @returns Transaction signature
233
+ * @deprecated Use deposit instead
235
234
  * @throws {NordError} If required parameters are missing or operation fails
236
235
  */
237
- async depositSpl(amount, tokenId) {
236
+ async depositSpl(amount, tokenId, recipient) {
237
+ return this.deposit({ amount, tokenId, recipient });
238
+ }
239
+ /**
240
+ * Deposit SPL tokens to the bridge
241
+ *
242
+ * @param amount - Amount to deposit
243
+ * @param tokenId - Token ID
244
+ * @param recipient - Recipient address; defaults to the user's address
245
+ * @returns Transaction signature
246
+ * @throws {NordError} If required parameters are missing or operation fails
247
+ */
248
+ async deposit({ amount, tokenId, recipient, }) {
238
249
  try {
239
250
  // Find the token info
240
251
  const tokenInfo = this.splTokenInfos.find((t) => t.tokenId === tokenId);
@@ -242,24 +253,22 @@ class NordUser {
242
253
  throw new NordError_1.NordError(`Token with ID ${tokenId} not found`);
243
254
  }
244
255
  const mint = new web3_js_1.PublicKey(tokenInfo.mint);
245
- // Get the user's token account
246
256
  const fromAccount = await this.getAssociatedTokenAccount(mint);
247
- // Convert amount to BN with proper decimals
248
- const amountBN = (0, proton_1.floatToBn)(amount, tokenInfo.precision);
249
- // Create deposit parameters
250
- const depositParams = {
251
- amount: amountBN,
257
+ const payer = this.getSolanaPublicKey();
258
+ const { ix, extraSigner } = await this.nord.protonClient.buildDepositIx({
259
+ payer,
260
+ recipient: recipient ?? payer,
261
+ quantAmount: (0, proton_1.floatToScaledBigIntLossy)(amount, tokenInfo.precision),
252
262
  mint,
253
- fromAccount,
254
- };
255
- // Build the deposit transaction using proton client
256
- const depositTx = await this.nord.protonClient.buildDepositTx(depositParams, this.getSolanaPublicKey(), this.connection);
257
- const { blockhash } = await this.connection.getLatestBlockhash();
258
- depositTx.recentBlockhash = blockhash;
259
- depositTx.feePayer = this.getSolanaPublicKey();
260
- const signedTx = await this.transactionSignFn(depositTx);
261
- // TODO: should use `VersionedTransaction` and remove any for `transactionSignFn`,
262
- // this is incredibly annoying to debug and i could've saved 30 mins.
263
+ sourceTokenAccount: fromAccount,
264
+ });
265
+ const { blockhash } = await this.connection.getLatestBlockhash("confirmed");
266
+ const tx = new web3_js_1.Transaction();
267
+ tx.add(ix);
268
+ tx.recentBlockhash = blockhash;
269
+ tx.feePayer = payer;
270
+ const signedTx = await this.transactionSignFn(tx);
271
+ signedTx.partialSign(extraSigner);
263
272
  const signature = await this.connection.sendRawTransaction(signedTx.serialize());
264
273
  return signature;
265
274
  }
@@ -285,9 +294,12 @@ class NordUser {
285
294
  if (!this.publicKey) {
286
295
  throw new NordError_1.NordError("Public key is required to update account ID");
287
296
  }
288
- const resp = await this.nord.getUserAccountIds({
297
+ const resp = await this.nord.getUser({
289
298
  pubkey: this.publicKey.toBase58(),
290
299
  });
300
+ if (!resp) {
301
+ throw new NordError_1.NordError(`User ${this.publicKey.toBase58()} not found`);
302
+ }
291
303
  this.accountIds = resp.accountIds;
292
304
  }
293
305
  catch (error) {
@@ -320,16 +332,6 @@ class NordUser {
320
332
  symbol: balance.token,
321
333
  });
322
334
  }
323
- // Process orders
324
- this.orders[accountData.accountId] = accountData.orders.map((order) => {
325
- return {
326
- orderId: order.orderId,
327
- isLong: order.side === "bid",
328
- size: order.size,
329
- price: order.price,
330
- marketId: order.marketId,
331
- };
332
- });
333
335
  // Process positions
334
336
  this.positions[accountData.accountId] = accountData.positions;
335
337
  // Process margins
@@ -343,7 +345,6 @@ class NordUser {
343
345
  * @throws {NordError} If the operation fails
344
346
  */
345
347
  async refreshSession() {
346
- console.log(this.publicKey);
347
348
  this.sessionId = await (0, actions_1.createSession)(this.nord.webServerUrl, this.walletSignFn, await this.nord.getTimestamp(), this.getNonce(), {
348
349
  userPubkey: (0, utils_1.optExpect)(this.publicKey.toBytes(), "No user's public key"),
349
350
  sessionPubkey: this.sessionPubKey,
@@ -384,7 +385,7 @@ class NordUser {
384
385
  * @param amount - Amount to withdraw
385
386
  * @throws {NordError} If the operation fails
386
387
  */
387
- async withdraw(tokenId, amount) {
388
+ async withdraw({ amount, tokenId, }) {
388
389
  try {
389
390
  this.checkSessionValidity();
390
391
  const { actionId } = await (0, actions_1.withdraw)(this.nord.webServerUrl, this.sessionSignFn, await this.nord.getTimestamp(), this.getNonce(), {
@@ -478,6 +479,62 @@ class NordUser {
478
479
  throw new NordError_1.NordError("Failed to transfer tokens", { cause: error });
479
480
  }
480
481
  }
482
+ /**
483
+ * Execute up to four place/cancel operations atomically.
484
+ * Per Market:
485
+ * 1. cancels can only be in the start (one cannot predict future order ids)
486
+ * 2. intermediate trades can trade only
487
+ * 3. placements go last
488
+ *
489
+ * Across Markets, order action can be any
490
+ *
491
+ * @param userActions array of user-friendly subactions
492
+ * @param providedAccountId optional account performing the action (defaults to first account)
493
+ */
494
+ async atomic(userActions, providedAccountId) {
495
+ try {
496
+ this.checkSessionValidity();
497
+ const accountId = providedAccountId != null ? providedAccountId : this.accountIds?.[0];
498
+ if (accountId == null) {
499
+ throw new NordError_1.NordError("Account ID is undefined. Make sure to call updateAccountId() before atomic operations.");
500
+ }
501
+ const apiActions = userActions.map((act) => {
502
+ if (act.kind === "place") {
503
+ const market = (0, utils_1.findMarket)(this.nord.markets, act.marketId);
504
+ if (!market) {
505
+ throw new NordError_1.NordError(`Market ${act.marketId} not found`);
506
+ }
507
+ return {
508
+ kind: "place",
509
+ marketId: act.marketId,
510
+ side: act.side,
511
+ fillMode: act.fillMode,
512
+ isReduceOnly: act.isReduceOnly,
513
+ sizeDecimals: market.sizeDecimals,
514
+ priceDecimals: market.priceDecimals,
515
+ size: act.size,
516
+ price: act.price,
517
+ quoteSizeSize: act.quoteSize, // treated as quote size; we pass only size component
518
+ quoteSizePrice: undefined,
519
+ clientOrderId: act.clientOrderId,
520
+ };
521
+ }
522
+ return {
523
+ kind: "cancel",
524
+ orderId: act.orderId,
525
+ };
526
+ });
527
+ const result = await (0, actions_1.atomic)(this.nord.webServerUrl, this.sessionSignFn, await this.nord.getTimestamp(), this.getNonce(), {
528
+ sessionId: (0, utils_1.optExpect)(this.sessionId, "No session"),
529
+ accountId: accountId,
530
+ actions: apiActions,
531
+ });
532
+ return result;
533
+ }
534
+ catch (error) {
535
+ throw new NordError_1.NordError("Atomic operation failed", { cause: error });
536
+ }
537
+ }
481
538
  /**
482
539
  * Helper function to retry a promise with exponential backoff
483
540
  *
@@ -3,7 +3,5 @@ export { NordUser } from "./client/NordUser";
3
3
  export { NordError } from "./utils/NordError";
4
4
  export * from "./api/core";
5
5
  export * from "./api/metrics";
6
- export * from "./api/queries";
7
- export * from "./api/market";
8
6
  export * from "./api/actions";
9
7
  export * from "./models/Subscriber";
@@ -26,8 +26,6 @@ Object.defineProperty(exports, "NordError", { enumerable: true, get: function ()
26
26
  // Export API modules
27
27
  __exportStar(require("./api/core"), exports);
28
28
  __exportStar(require("./api/metrics"), exports);
29
- __exportStar(require("./api/queries"), exports);
30
- __exportStar(require("./api/market"), exports);
31
29
  __exportStar(require("./api/actions"), exports);
32
30
  // Export models
33
31
  __exportStar(require("./models/Subscriber"), exports);
@@ -1,5 +1,5 @@
1
1
  import { EventEmitter } from "events";
2
- import { Account, DeltaEvent, OrderbookResponse, SubscriberConfig, Trade, Trades } from "../../types";
2
+ import { Account, DeltaEvent, OrderbookResponse, SubscriberConfig, StreamTrade, Trades } from "../../types";
3
3
  /**
4
4
  * Subscriber class for handling WebSocket subscriptions
5
5
  */
@@ -30,7 +30,7 @@ export interface OrderbookSubscription extends EventEmitter {
30
30
  * Interface for trade subscription
31
31
  */
32
32
  export interface TradeSubscription extends EventEmitter {
33
- on(event: "message", listener: (data: Trade[]) => void): this;
33
+ on(event: "message", listener: (data: StreamTrade[]) => void): this;
34
34
  on(event: "error", listener: (error: Error) => void): this;
35
35
  close(): void;
36
36
  removeAllListeners(event?: string): this;