@n1xyz/nord-ts 0.0.12 → 0.0.15

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 (188) hide show
  1. package/.prettierignore +1 -0
  2. package/README.md +9 -6
  3. package/dist/bridge/client.d.ts +8 -7
  4. package/dist/bridge/client.js +38 -37
  5. package/dist/bridge/index.d.ts +1 -2
  6. package/dist/bridge/index.js +2 -2
  7. package/dist/bridge/types.d.ts +5 -3
  8. package/dist/bridge/types.js +3 -1
  9. package/dist/gen/nord.d.ts +66 -5
  10. package/dist/gen/nord.js +449 -22
  11. package/dist/idl/bridge.json +1493 -0
  12. package/dist/idl/index.d.ts +585 -0
  13. package/dist/idl/index.js +8 -0
  14. package/dist/nord/api/core.d.ts +8 -8
  15. package/dist/nord/api/core.js +58 -15
  16. package/dist/nord/api/metrics.js +7 -4
  17. package/dist/nord/client/Nord.d.ts +57 -72
  18. package/dist/nord/client/Nord.js +134 -125
  19. package/dist/nord/client/NordUser.d.ts +0 -17
  20. package/dist/nord/client/NordUser.js +4 -35
  21. package/dist/types.d.ts +38 -15
  22. package/dist/types.js +2 -2
  23. package/dist/websocket/NordWebSocketClient.d.ts +0 -3
  24. package/dist/websocket/NordWebSocketClient.js +10 -13
  25. package/dist/websocket/events.d.ts +2 -2
  26. package/docs/assets/hierarchy.js +1 -1
  27. package/docs/assets/navigation.js +1 -1
  28. package/docs/assets/search.js +1 -1
  29. package/docs/classes/Nord.html +99 -100
  30. package/docs/classes/NordError.html +6 -6
  31. package/docs/classes/NordUser.html +48 -53
  32. package/docs/classes/NordWebSocketClient.html +6 -11
  33. package/docs/classes/SolanaBridgeClient.html +23 -23
  34. package/docs/classes/Subscriber.html +3 -3
  35. package/docs/enums/FillMode.html +2 -2
  36. package/docs/enums/KeyType.html +2 -2
  37. package/docs/enums/MetricPeriod.html +2 -2
  38. package/docs/enums/PdaSeedType.html +2 -2
  39. package/docs/enums/PeakTpsPeriodUnit.html +2 -2
  40. package/docs/enums/Side.html +2 -2
  41. package/docs/enums/WebSocketMessageType.html +3 -3
  42. package/docs/functions/actionQueryRollman.html +1 -1
  43. package/docs/functions/actionsQueryRollman.html +1 -1
  44. package/docs/functions/aggregateMetrics-1.html +1 -1
  45. package/docs/functions/assert.html +1 -1
  46. package/docs/functions/bigIntToProtoU128.html +1 -1
  47. package/docs/functions/bridgeToBN.html +1 -1
  48. package/docs/functions/bufferToHex.html +1 -1
  49. package/docs/functions/cancelOrder.html +1 -1
  50. package/docs/functions/checkPubKeyLength.html +1 -1
  51. package/docs/functions/checkedFetch.html +1 -1
  52. package/docs/functions/createSession.html +1 -1
  53. package/docs/functions/decodeLengthDelimited.html +1 -1
  54. package/docs/functions/encodeLengthDelimited.html +1 -1
  55. package/docs/functions/fillModeToProtoFillMode.html +1 -1
  56. package/docs/functions/findMarket.html +1 -1
  57. package/docs/functions/findPda.html +1 -1
  58. package/docs/functions/findToken.html +1 -1
  59. package/docs/functions/fromBN.html +1 -1
  60. package/docs/functions/getAccount.html +1 -1
  61. package/docs/functions/getActionNonce.html +1 -1
  62. package/docs/functions/getCurrentTps.html +1 -1
  63. package/docs/functions/getInfo.html +1 -1
  64. package/docs/functions/{queryLastNBlocks.html → getLastActionId.html} +3 -3
  65. package/docs/functions/getMedianLatency.html +1 -1
  66. package/docs/functions/getOrderbook.html +1 -1
  67. package/docs/functions/getPeakTps.html +1 -1
  68. package/docs/functions/getTimestamp.html +1 -1
  69. package/docs/functions/getTotalTransactions.html +1 -1
  70. package/docs/functions/getTrades.html +1 -1
  71. package/docs/functions/getUserAccountIds.html +1 -1
  72. package/docs/functions/hexToBuffer.html +1 -1
  73. package/docs/functions/initWebSocketClient.html +9 -9
  74. package/docs/functions/keypairFromPrivateKey.html +1 -1
  75. package/docs/functions/makeSigningFunction.html +1 -1
  76. package/docs/functions/makeWalletSignFn.html +1 -1
  77. package/docs/functions/marketsStats.html +1 -1
  78. package/docs/functions/optExpect.html +1 -1
  79. package/docs/functions/optMap.html +1 -1
  80. package/docs/functions/optUnwrap.html +1 -1
  81. package/docs/functions/panic.html +1 -1
  82. package/docs/functions/placeOrder.html +1 -1
  83. package/docs/functions/queryAction.html +1 -1
  84. package/docs/functions/queryPrometheus.html +1 -1
  85. package/docs/functions/queryRecentActions.html +4 -3
  86. package/docs/functions/revokeSession.html +1 -1
  87. package/docs/functions/shortenPublicKey.html +1 -1
  88. package/docs/functions/signAction.html +1 -1
  89. package/docs/functions/toBN.html +1 -1
  90. package/docs/functions/toScaledU128.html +1 -1
  91. package/docs/functions/toScaledU64.html +1 -1
  92. package/docs/functions/transfer.html +1 -1
  93. package/docs/functions/withdraw.html +1 -1
  94. package/docs/hierarchy.html +1 -1
  95. package/docs/index.html +2 -2
  96. package/docs/interfaces/Account.html +2 -2
  97. package/docs/interfaces/ActionInfo.html +2 -2
  98. package/docs/interfaces/ActionNonceResponse.html +2 -2
  99. package/docs/interfaces/ActionQuery.html +2 -2
  100. package/docs/interfaces/ActionResponse.html +2 -2
  101. package/docs/interfaces/ActionsExtendedInfo.html +2 -2
  102. package/docs/interfaces/ActionsQuery.html +2 -2
  103. package/docs/interfaces/ActionsResponse.html +2 -2
  104. package/docs/interfaces/AggregateMetrics.html +2 -2
  105. package/docs/interfaces/BlockFacts.html +5 -5
  106. package/docs/interfaces/BlockQuery.html +2 -2
  107. package/docs/interfaces/BlockResponse.html +2 -2
  108. package/docs/interfaces/BlockSummary.html +2 -2
  109. package/docs/interfaces/BlockSummaryResponse.html +2 -2
  110. package/docs/interfaces/DeltaEvent.html +2 -2
  111. package/docs/interfaces/DepositSplParams.html +5 -5
  112. package/docs/interfaces/Info.html +2 -2
  113. package/docs/interfaces/Market.html +2 -2
  114. package/docs/interfaces/MarketStats.html +2 -2
  115. package/docs/interfaces/MarketsStatsResponse.html +2 -2
  116. package/docs/interfaces/NordConfig.html +19 -9
  117. package/docs/interfaces/NordWebSocketClientEvents.html +2 -2
  118. package/docs/interfaces/NordWebSocketEvents.html +3 -3
  119. package/docs/interfaces/Order.html +2 -2
  120. package/docs/interfaces/OrderInfo.html +2 -2
  121. package/docs/interfaces/OrderbookEntry.html +2 -2
  122. package/docs/interfaces/OrderbookQuery.html +2 -2
  123. package/docs/interfaces/OrderbookResponse.html +2 -2
  124. package/docs/interfaces/OrderbookSubscription.html +5 -5
  125. package/docs/interfaces/PerpMarketStats.html +2 -2
  126. package/docs/interfaces/RollmanActionExtendedInfo.html +2 -2
  127. package/docs/interfaces/RollmanActionInfo.html +2 -2
  128. package/docs/interfaces/RollmanActionResponse.html +2 -2
  129. package/docs/interfaces/RollmanActionsResponse.html +2 -2
  130. package/docs/interfaces/RollmanBlockResponse.html +2 -2
  131. package/docs/interfaces/SPLTokenInfo.html +5 -5
  132. package/docs/interfaces/SolanaBridgeConfig.html +5 -5
  133. package/docs/interfaces/StateFacts.html +5 -5
  134. package/docs/interfaces/SubscriberConfig.html +2 -2
  135. package/docs/interfaces/TimestampResponse.html +2 -2
  136. package/docs/interfaces/Token.html +2 -2
  137. package/docs/interfaces/TokenInfo.html +3 -2
  138. package/docs/interfaces/Trade.html +2 -2
  139. package/docs/interfaces/TradeSubscription.html +5 -5
  140. package/docs/interfaces/Trades.html +2 -2
  141. package/docs/interfaces/TradesQuery.html +2 -2
  142. package/docs/interfaces/TradesResponse.html +2 -2
  143. package/docs/interfaces/TransferParams.html +4 -4
  144. package/docs/interfaces/UserAccountIdsQuery.html +2 -2
  145. package/docs/interfaces/UserAccountIdsResponse.html +2 -2
  146. package/docs/interfaces/WebSocketAccountUpdate.html +6 -0
  147. package/docs/interfaces/WebSocketDeltaUpdate.html +3 -3
  148. package/docs/interfaces/WebSocketSubscription.html +3 -3
  149. package/docs/interfaces/WebSocketTradeUpdate.html +3 -3
  150. package/docs/interfaces/WithdrawalClaim.html +7 -7
  151. package/docs/interfaces/WithdrawalParams.html +4 -4
  152. package/docs/modules.html +1 -1
  153. package/docs/types/BigIntValue.html +1 -1
  154. package/docs/types/SubscriptionPattern.html +4 -0
  155. package/docs/types/SubscriptionType.html +2 -0
  156. package/docs/types/WebSocketMessage.html +1 -1
  157. package/docs/variables/DEBUG_KEYS.html +1 -1
  158. package/docs/variables/DEFAULT_FUNDING_AMOUNTS.html +1 -1
  159. package/docs/variables/DEV_TOKEN_INFOS.html +1 -1
  160. package/docs/variables/DEV_URL.html +1 -1
  161. package/docs/variables/MAX_BUFFER_LEN.html +1 -1
  162. package/docs/variables/SESSION_TTL.html +1 -1
  163. package/docs/variables/WEBSERVER_DEV_URL.html +1 -1
  164. package/docs/variables/ZERO_DECIMAL.html +1 -1
  165. package/docs/variables/_private.html +1 -1
  166. package/idl-generate.sh +4 -0
  167. package/package.json +4 -3
  168. package/src/bridge/client.ts +54 -43
  169. package/src/bridge/index.ts +1 -2
  170. package/src/bridge/types.ts +5 -3
  171. package/src/gen/nord.ts +518 -19
  172. package/src/idl/bridge.json +1493 -0
  173. package/src/idl/index.ts +2 -0
  174. package/src/nord/api/core.ts +70 -20
  175. package/src/nord/api/metrics.ts +28 -20
  176. package/src/nord/api/queries.ts +7 -11
  177. package/src/nord/client/Nord.ts +174 -168
  178. package/src/nord/client/NordUser.ts +32 -98
  179. package/src/types.ts +40 -15
  180. package/src/websocket/NordWebSocketClient.ts +11 -14
  181. package/src/websocket/events.ts +2 -2
  182. package/docs/functions/blockQueryRollman.html +0 -6
  183. package/docs/functions/blockSummaryQueryRollman.html +0 -6
  184. package/docs/functions/queryBlock.html +0 -6
  185. package/docs/functions/queryRecentBlocks.html +0 -6
  186. package/docs/interfaces/WebSocketUserUpdate.html +0 -6
  187. package/src/idl/bridge.ts +0 -930
  188. package/test.ts +0 -107
@@ -1,4 +1,5 @@
1
1
  import { EventEmitter } from "events";
2
+ import { Connection, PublicKey } from "@solana/web3.js";
2
3
  import {
3
4
  Account,
4
5
  ActionQuery,
@@ -14,11 +15,12 @@ import {
14
15
  PeakTpsPeriodUnit,
15
16
  RollmanActionResponse,
16
17
  RollmanActionsResponse,
18
+ SubscriptionPattern,
17
19
  Token,
18
20
  TradesQuery,
19
21
  TradesResponse,
20
22
  UserAccountIdsQuery,
21
- UserAccountIdsResponse
23
+ UserAccountIdsResponse,
22
24
  } from "../../types";
23
25
  import { NordWebSocketClient } from "../../websocket/index";
24
26
  import * as core from "../api/core";
@@ -35,6 +37,18 @@ export interface UserSubscription extends EventEmitter {
35
37
  close: () => void;
36
38
  }
37
39
 
40
+ /**
41
+ * WebSocket subscription options interface
42
+ */
43
+ export interface WebSocketSubscriptionOptions {
44
+ /** Market symbols to subscribe to for trade updates */
45
+ trades?: string[];
46
+ /** Market symbols to subscribe to for orderbook delta updates */
47
+ deltas?: string[];
48
+ /** Account IDs to subscribe to for account updates */
49
+ accounts?: number[];
50
+ }
51
+
38
52
  /**
39
53
  * Main Nord client class for interacting with the Nord API
40
54
  */
@@ -42,8 +56,11 @@ export class Nord {
42
56
  /** Base URL for the Nord web server */
43
57
  public readonly webServerUrl: string;
44
58
 
45
- /** Solana program ID */
46
- public readonly solanaProgramId: string;
59
+ /** Bridge verification key */
60
+ public readonly bridgeVk: string;
61
+
62
+ /** Optional Solana program ID (will be derived from bridgeVk) */
63
+ private _solanaProgramId!: string;
47
64
 
48
65
  /** Solana RPC URL */
49
66
  public readonly solanaUrl: string;
@@ -57,113 +74,92 @@ export class Nord {
57
74
  /** Map of symbol to market_id */
58
75
  private symbolToMarketId: Map<string, number> = new Map();
59
76
 
60
- /**
61
- * WebSocket client for trades
62
- * @private
63
- */
64
- private tradesWs: NordWebSocketClient | null = null;
65
-
66
- /**
67
- * WebSocket client for orderbook deltas
68
- * @private
69
- */
70
- private deltasWs: NordWebSocketClient | null = null;
71
-
72
- /**
73
- * WebSocket client for user updates
74
- * @private
75
- */
76
- private userWs: NordWebSocketClient | null = null;
77
-
78
- /**
79
- * Initial subscriptions for the trades WebSocket
80
- * @private
81
- */
82
- private tradesSubscriptions?: string[];
83
-
84
- /**
85
- * Initial subscriptions for the deltas WebSocket
86
- * @private
87
- */
88
- private deltasSubscriptions?: string[];
89
-
90
77
  /**
91
78
  * Create a new Nord client
92
79
  *
93
80
  * @param config - Configuration options for the Nord client
94
81
  * @param config.webServerUrl - Base URL for the Nord web server
95
- * @param config.solanaProgramId - Solana program ID
82
+ * @param config.bridgeVk - Bridge verification key
96
83
  * @param config.solanaUrl - Solana cluster URL
97
- * @param config.initWebSockets - Whether to initialize WebSockets on creation, defaults to true
98
- * @param config.tradesSubscriptions - Optional array of trades subscriptions to initialize with (e.g., ["trades@BTCUSDC"])
99
- * @param config.deltasSubscriptions - Optional array of deltas subscriptions to initialize with (e.g., ["deltas@BTCUSDC"])
100
84
  * @throws {Error} If required configuration is missing
101
85
  */
102
- constructor({
103
- webServerUrl,
104
- solanaProgramId,
105
- solanaUrl,
106
- initWebSockets = true,
107
- tradesSubscriptions,
108
- deltasSubscriptions,
109
- }: NordConfig) {
110
- if (!webServerUrl) {
86
+ constructor(config: NordConfig) {
87
+ if (!config.webServerUrl) {
111
88
  throw new NordError("webServerUrl is required");
112
89
  }
113
90
 
114
- if (!solanaProgramId) {
115
- throw new NordError("solanaProgramId is required");
91
+ if (!config.bridgeVk) {
92
+ throw new NordError("bridgeVk is required");
116
93
  }
117
94
 
118
- if (!solanaUrl) {
95
+ if (!config.solanaUrl) {
119
96
  throw new NordError("solanaUrl is required");
120
97
  }
121
98
 
122
- this.webServerUrl = webServerUrl;
123
- this.solanaProgramId = solanaProgramId;
124
- this.solanaUrl = solanaUrl;
125
-
126
- // Store subscription parameters
127
- this.tradesSubscriptions = tradesSubscriptions;
128
- this.deltasSubscriptions = deltasSubscriptions;
129
-
130
- // Initialize WebSocket clients only if initWebSockets is true
131
- if (initWebSockets) {
132
- this.initializeWebSockets();
133
- }
99
+ this.webServerUrl = config.webServerUrl;
100
+ this.bridgeVk = config.bridgeVk;
101
+ this.solanaUrl = config.solanaUrl;
134
102
  }
135
103
 
136
104
  /**
137
- * Initialize WebSocket clients for trades and deltas
105
+ * Create a WebSocket client with specific subscriptions
138
106
  *
139
- * This method can be called manually if websockets were not initialized during construction
140
- * (i.e., if initWebSockets was set to false in the constructor).
107
+ * @param options - Subscription options that specify which data streams to subscribe to
108
+ * @returns A new WebSocket client with the requested subscriptions
109
+ * @throws {NordError} If invalid subscription options are provided
141
110
  *
142
- * It initializes the trades and deltas WebSocket clients. The user WebSocket client
143
- * is initialized on demand when needed.
111
+ * @example
112
+ * // Create a client for trades and deltas from one market and an account
113
+ * const wsClient = nord.createWebSocketClient({
114
+ * trades: ["BTCUSDC"],
115
+ * deltas: ["BTCUSDC"],
116
+ * accounts: [123]
117
+ * });
144
118
  *
145
- * @param tradesSubscriptions - Optional array of trades subscriptions to initialize with
146
- * @param deltasSubscriptions - Optional array of deltas subscriptions to initialize with
119
+ * @example
120
+ * // Create a client for trades from multiple markets
121
+ * const tradesClient = nord.createWebSocketClient({
122
+ * trades: ["BTCUSDC", "ETHUSDC"]
123
+ * });
147
124
  */
148
- public initializeWebSockets(
149
- tradesSubscriptions?: string[],
150
- deltasSubscriptions?: string[],
151
- ): void {
152
- // Use provided subscriptions or fall back to stored ones
153
- const trades = tradesSubscriptions || this.tradesSubscriptions;
154
- const deltas = deltasSubscriptions || this.deltasSubscriptions;
155
-
156
- // Initialize WebSocket clients for each endpoint with subscriptions
157
- this.tradesWs = core.initWebSocketClient(
158
- this.webServerUrl,
159
- "trades",
160
- trades,
161
- );
162
- this.deltasWs = core.initWebSocketClient(
163
- this.webServerUrl,
164
- "deltas",
165
- deltas,
166
- );
125
+ public createWebSocketClient(
126
+ options: WebSocketSubscriptionOptions,
127
+ ): NordWebSocketClient {
128
+ const subscriptions: SubscriptionPattern[] = [];
129
+
130
+ // Add trade subscriptions
131
+ if (options.trades && options.trades.length > 0) {
132
+ options.trades.forEach((symbol) => {
133
+ subscriptions.push(`trades@${symbol}` as SubscriptionPattern);
134
+ });
135
+ }
136
+
137
+ // Add delta subscriptions
138
+ if (options.deltas && options.deltas.length > 0) {
139
+ options.deltas.forEach((symbol) => {
140
+ subscriptions.push(`deltas@${symbol}` as SubscriptionPattern);
141
+ });
142
+ }
143
+
144
+ // Add account subscriptions
145
+ if (options.accounts && options.accounts.length > 0) {
146
+ options.accounts.forEach((accountId) => {
147
+ if (isNaN(accountId) || accountId <= 0) {
148
+ throw new NordError(
149
+ `Invalid account ID: ${accountId}. Must be a positive number.`,
150
+ );
151
+ }
152
+ subscriptions.push(`account@${accountId}` as SubscriptionPattern);
153
+ });
154
+ }
155
+
156
+ // Validate that at least one subscription was provided
157
+ if (subscriptions.length === 0) {
158
+ throw new NordError("At least one subscription must be provided");
159
+ }
160
+
161
+ // Create and return a new WebSocket client
162
+ return core.initWebSocketClient(this.webServerUrl, subscriptions);
167
163
  }
168
164
 
169
165
  /**
@@ -212,20 +208,49 @@ export class Nord {
212
208
  *
213
209
  * @param nordConfig - Configuration options for the Nord client
214
210
  * @param nordConfig.webServerUrl - Base URL for the Nord web server
215
- * @param nordConfig.solanaProgramId - Solana program ID
211
+ * @param nordConfig.bridgeVk - Bridge verification key
216
212
  * @param nordConfig.solanaUrl - Solana cluster URL
217
- * @param nordConfig.initWebSockets - Whether to initialize WebSockets on creation, defaults to true
218
- * @param nordConfig.tradesSubscriptions - Optional array of trades subscriptions (e.g., ["trades@BTCUSDC"])
219
- * @param nordConfig.deltasSubscriptions - Optional array of deltas subscriptions (e.g., ["deltas@BTCUSDC"])
220
213
  * @returns Initialized Nord client
221
214
  * @throws {NordError} If initialization fails
222
215
  */
223
216
  public static async initNord(nordConfig: NordConfig): Promise<Nord> {
224
217
  const nord = new Nord(nordConfig);
225
- await nord.fetchNordInfo();
218
+ await nord.init();
226
219
  return nord;
227
220
  }
228
221
 
222
+ /**
223
+ * Initialize the Nord client by deriving program ID and fetching info
224
+ * @private
225
+ */
226
+ private async init(): Promise<void> {
227
+ const connection = new Connection(this.solanaUrl);
228
+ const bridgeVkPubkey = new PublicKey(this.bridgeVk);
229
+
230
+ const bridgeAccount = await connection.getAccountInfo(bridgeVkPubkey);
231
+ if (!bridgeAccount) {
232
+ throw new NordError(`Bridge account ${this.bridgeVk} not found`);
233
+ }
234
+
235
+ this._solanaProgramId = bridgeAccount.owner.toString();
236
+ await this.fetchNordInfo();
237
+ }
238
+
239
+ /**
240
+ * Get the Solana program ID derived from bridge VK
241
+ *
242
+ * @returns Program ID string
243
+ * @throws {NordError} If program ID hasn't been initialized
244
+ */
245
+ public getSolanaProgramId(): string {
246
+ if (!this._solanaProgramId) {
247
+ throw new NordError(
248
+ "Solana program ID not initialized. Have you called Nord.initNord()?",
249
+ );
250
+ }
251
+ return this._solanaProgramId;
252
+ }
253
+
229
254
  /**
230
255
  * Get market statistics
231
256
  *
@@ -268,7 +293,7 @@ export class Nord {
268
293
  async getLastActionId(): Promise<number> {
269
294
  return queries.getLastActionId(this.webServerUrl);
270
295
  }
271
-
296
+
272
297
  /**
273
298
  * Fetch aggregate metrics from the Nord API
274
299
  *
@@ -364,68 +389,22 @@ export class Nord {
364
389
  return metrics.queryPrometheus(this.webServerUrl, params);
365
390
  }
366
391
 
367
- /**
368
- * Get the trades WebSocket client (default)
369
- * If not already initialized, it will be created
370
- *
371
- * @returns WebSocket client for trades
372
- */
373
- public getWebSocketClient(): NordWebSocketClient {
374
- if (!this.tradesWs) {
375
- this.initializeWebSockets();
376
- }
377
- return this.tradesWs!;
378
- }
379
-
380
- /**
381
- * Get the trades WebSocket client
382
- * If not already initialized, it will be created
383
- *
384
- * @returns WebSocket client for trades
385
- */
386
- public getTradesWebSocketClient(): NordWebSocketClient {
387
- if (!this.tradesWs) {
388
- this.initializeWebSockets();
389
- }
390
- return this.tradesWs!;
391
- }
392
-
393
- /**
394
- * Get the deltas WebSocket client
395
- * If not already initialized, it will be created
396
- *
397
- * @returns WebSocket client for orderbook deltas
398
- */
399
- public getDeltasWebSocketClient(): NordWebSocketClient {
400
- if (!this.deltasWs) {
401
- this.initializeWebSockets();
402
- }
403
- return this.deltasWs!;
404
- }
405
-
406
- /**
407
- * Get the user WebSocket client
408
- * If not already initialized, it will be created
409
- *
410
- * @returns WebSocket client for user updates
411
- */
412
- public getUserWebSocketClient(): NordWebSocketClient {
413
- if (!this.userWs) {
414
- // Initialize user WebSocket client on demand
415
- this.userWs = core.initWebSocketClient(this.webServerUrl, "user");
416
- return this.userWs;
417
- }
418
- return this.userWs;
419
- }
420
-
421
392
  /**
422
393
  * Subscribe to orderbook updates for a market
423
394
  *
424
395
  * @param symbol - Market symbol
425
396
  * @returns Orderbook subscription
397
+ * @throws {NordError} If symbol is invalid
426
398
  */
427
399
  public subscribeOrderbook(symbol: string): OrderbookSubscription {
400
+ if (!symbol || typeof symbol !== "string") {
401
+ throw new NordError("Invalid market symbol");
402
+ }
403
+
428
404
  const subscription = new EventEmitter() as OrderbookSubscription;
405
+ const wsClient = this.createWebSocketClient({
406
+ deltas: [symbol],
407
+ });
429
408
 
430
409
  const handleDelta = (update: {
431
410
  symbol: string;
@@ -439,17 +418,11 @@ export class Nord {
439
418
  subscription.emit("message", update);
440
419
  };
441
420
 
442
- // Initialize deltas websocket if it doesn't exist
443
- if (!this.deltasWs) {
444
- this.initializeWebSockets();
445
- }
446
-
447
- this.deltasWs!.on("delta", handleDelta);
448
- this.deltasWs!.subscribe([`deltas@${symbol}`]);
421
+ wsClient.on("delta", handleDelta);
449
422
 
450
423
  subscription.close = () => {
451
- this.deltasWs!.unsubscribe([`deltas@${symbol}`]);
452
- this.deltasWs!.removeListener("delta", handleDelta);
424
+ wsClient.unsubscribe([`deltas@${symbol}`]);
425
+ wsClient.removeListener("delta", handleDelta);
453
426
  subscription.removeAllListeners();
454
427
  };
455
428
 
@@ -461,9 +434,17 @@ export class Nord {
461
434
  *
462
435
  * @param symbol - Market symbol
463
436
  * @returns Trade subscription
437
+ * @throws {NordError} If symbol is invalid
464
438
  */
465
439
  public subscribeTrades(symbol: string): TradeSubscription {
440
+ if (!symbol || typeof symbol !== "string") {
441
+ throw new NordError("Invalid market symbol");
442
+ }
443
+
466
444
  const subscription = new EventEmitter() as TradeSubscription;
445
+ const wsClient = this.createWebSocketClient({
446
+ trades: [symbol],
447
+ });
467
448
 
468
449
  const handleTrade = (update: {
469
450
  symbol: string;
@@ -481,17 +462,47 @@ export class Nord {
481
462
  subscription.emit("message", update);
482
463
  };
483
464
 
484
- // Initialize trades websocket if it doesn't exist
485
- if (!this.tradesWs) {
486
- this.initializeWebSockets();
465
+ wsClient.on("trades", handleTrade);
466
+
467
+ subscription.close = () => {
468
+ wsClient.unsubscribe([`trades@${symbol}`]);
469
+ wsClient.removeListener("trades", handleTrade);
470
+ subscription.removeAllListeners();
471
+ };
472
+
473
+ return subscription;
474
+ }
475
+
476
+ /**
477
+ * Subscribe to account updates
478
+ *
479
+ * @param accountId - Account ID to subscribe to
480
+ * @returns User subscription
481
+ * @throws {NordError} If accountId is invalid
482
+ */
483
+ public subscribeAccount(accountId: number): UserSubscription {
484
+ if (isNaN(accountId) || accountId <= 0) {
485
+ throw new NordError("Invalid account ID");
487
486
  }
488
487
 
489
- this.tradesWs!.on("trade", handleTrade);
490
- this.tradesWs!.subscribe([`trades@${symbol}`]);
488
+ const subscription = new EventEmitter() as UserSubscription;
489
+ const wsClient = this.createWebSocketClient({
490
+ accounts: [accountId],
491
+ });
492
+
493
+ const handleAccountUpdate = (update: any) => {
494
+ if (update.account_id !== accountId) {
495
+ return;
496
+ }
497
+
498
+ subscription.emit("message", update);
499
+ };
500
+
501
+ wsClient.on("account", handleAccountUpdate);
491
502
 
492
503
  subscription.close = () => {
493
- this.tradesWs!.unsubscribe([`trades@${symbol}`]);
494
- this.tradesWs!.removeListener("trade", handleTrade);
504
+ wsClient.unsubscribe([`account@${accountId}`]);
505
+ wsClient.removeListener("account", handleAccountUpdate);
495
506
  subscription.removeAllListeners();
496
507
  };
497
508
 
@@ -547,11 +558,6 @@ export class Nord {
547
558
  query = { market_id: marketId };
548
559
  }
549
560
 
550
- // Ensure market_id is provided
551
- if (query.market_id === undefined) {
552
- throw new NordError("market_id is required for orderbook query");
553
- }
554
-
555
561
  return market.getOrderbook(this.webServerUrl, query);
556
562
  }
557
563
 
@@ -111,17 +111,6 @@ export interface TransferParams {
111
111
  toAccountId: number;
112
112
  }
113
113
 
114
- /**
115
- * Parameters for creating a new account
116
- */
117
- export interface CreateAccountParams {
118
- /** Token ID for initial funding */
119
- tokenId: number;
120
-
121
- /** Initial funding amount */
122
- amount: Decimal.Value;
123
- }
124
-
125
114
  /**
126
115
  * User class for interacting with the Nord protocol
127
116
  */
@@ -311,7 +300,7 @@ export class NordUser {
311
300
  if (
312
301
  !this.getSolanaPublicKey() ||
313
302
  !this.connection ||
314
- !this.nord.solanaProgramId
303
+ !this.nord.getSolanaProgramId()
315
304
  ) {
316
305
  throw new NordError(
317
306
  "Solana public key, connection, and program ID are required to initialize bridge client",
@@ -342,9 +331,10 @@ export class NordUser {
342
331
  this.bridgeClient = new SolanaBridgeClient(
343
332
  {
344
333
  rpcUrl: this.connection.rpcEndpoint,
345
- programId: this.nord.solanaProgramId,
334
+ programId: this.nord.getSolanaProgramId(),
346
335
  commitment: "confirmed",
347
336
  tokenInfos: this.splTokenInfos,
337
+ bridgeVk: this.nord.bridgeVk,
348
338
  },
349
339
  wallet,
350
340
  );
@@ -473,16 +463,6 @@ export class NordUser {
473
463
  const mint = new PublicKey(tokenInfo.mint);
474
464
  // Get the user's token account
475
465
  const fromAccount = await this.getAssociatedTokenAccount(mint);
476
-
477
- // Get the bridge's token account
478
- const [authority] = await this.bridgeClient.findAuthorityPda();
479
- const toAccount = await getAssociatedTokenAddress(
480
- mint,
481
- authority,
482
- true,
483
- TOKEN_2022_PROGRAM_ID,
484
- );
485
-
486
466
  // Convert amount to BN with proper decimals
487
467
  const amountBN = utilsToBN(amount, tokenInfo.precision);
488
468
 
@@ -491,7 +471,6 @@ export class NordUser {
491
471
  amount: amountBN,
492
472
  mint,
493
473
  fromAccount,
494
- toAccount,
495
474
  });
496
475
  } catch (error) {
497
476
  throw new NordError(
@@ -599,23 +578,23 @@ export class NordUser {
599
578
  * @throws {NordError} If the operation fails
600
579
  */
601
580
  async fetchInfo(): Promise<void> {
602
- interface FetchOrder {
581
+ type OpenOrder = {
603
582
  orderId: number;
604
- size: number;
605
- price: number;
606
583
  marketId: number;
607
584
  side: "ask" | "bid";
608
- originalOrderSize?: number;
585
+ size: number;
586
+ price: number;
587
+ originalOrderSize: number;
609
588
  clientOrderId?: number | null;
610
- }
589
+ };
611
590
 
612
- interface Balance {
591
+ type Balance = {
613
592
  tokenId: number;
614
593
  token: string;
615
594
  amount: number;
616
- }
595
+ };
617
596
 
618
- interface Position {
597
+ type Position = {
619
598
  marketId: number;
620
599
  openOrders: number;
621
600
  perp?: {
@@ -627,9 +606,9 @@ export class NordUser {
627
606
  isLong: boolean;
628
607
  };
629
608
  actionId: number;
630
- }
609
+ };
631
610
 
632
- interface Margins {
611
+ type Margins = {
633
612
  omf: number;
634
613
  mf: number;
635
614
  imf: number;
@@ -638,32 +617,31 @@ export class NordUser {
638
617
  pon: number;
639
618
  pn: number;
640
619
  bankruptcy: boolean;
641
- }
620
+ };
642
621
 
643
- interface Account {
622
+ type Account = {
644
623
  updateId: number;
645
- orders: FetchOrder[];
624
+ orders: OpenOrder[];
646
625
  positions: Position[];
647
626
  balances: Balance[];
648
627
  margins: Margins;
649
- actionNonce?: number | null;
650
- accountId: number;
651
- }
628
+ };
652
629
 
653
630
  if (this.accountIds !== undefined) {
654
- const accountsData = await Promise.all(
655
- this.accountIds.map(async (accountId) => {
656
- const response = await checkedFetch(
657
- `${this.nord.webServerUrl}/account?account_id=${accountId}`,
658
- );
659
- const accountData = (await response.json()) as Account;
660
- // Ensure we have the correct accountId
661
- return {
662
- ...accountData,
663
- accountId,
664
- };
665
- }),
666
- );
631
+ const accountsData: (Account & { accountId: number })[] =
632
+ await Promise.all(
633
+ this.accountIds.map(async (accountId) => {
634
+ const response = await checkedFetch(
635
+ `${this.nord.webServerUrl}/account/${accountId}`,
636
+ );
637
+ const accountData = (await response.json()) as Account;
638
+ // Ensure we have the correct accountId
639
+ return {
640
+ ...accountData,
641
+ accountId,
642
+ };
643
+ }),
644
+ );
667
645
 
668
646
  for (const accountData of accountsData) {
669
647
  // Process balances
@@ -678,13 +656,7 @@ export class NordUser {
678
656
 
679
657
  // Process orders
680
658
  this.orders[accountData.accountId] = accountData.orders.map(
681
- (order: {
682
- orderId: number;
683
- side: string;
684
- size: number;
685
- price: number;
686
- marketId: number;
687
- }) => {
659
+ (order: OpenOrder) => {
688
660
  return {
689
661
  orderId: order.orderId,
690
662
  isLong: order.side === "bid",
@@ -892,44 +864,6 @@ export class NordUser {
892
864
  }
893
865
  }
894
866
 
895
- /**
896
- * Create a new account
897
- *
898
- * @param params - Account creation parameters
899
- * @returns New NordUser instance
900
- * @throws {NordError} If the operation fails
901
- */
902
- async createAccount(params: CreateAccountParams): Promise<NordUser> {
903
- try {
904
- this.checkSessionValidity();
905
- // Create a new keypair for the account
906
- const keypair = Keypair.generate();
907
-
908
- // Create a new NordUser
909
- const newUser = NordUser.fromPrivateKey(
910
- this.nord,
911
- keypair.secretKey,
912
- this.connection,
913
- );
914
-
915
- // Transfer initial funds
916
- await this.transferToAccount({
917
- to: newUser,
918
- tokenId: params.tokenId,
919
- amount: params.amount,
920
- fromAccountId: optExpect(this.accountIds?.[0], "No account ID"),
921
- toAccountId: optExpect(
922
- newUser.accountIds?.[0],
923
- "No account ID for new user",
924
- ),
925
- });
926
-
927
- return newUser;
928
- } catch (error) {
929
- throw new NordError("Failed to create account", { cause: error });
930
- }
931
- }
932
-
933
867
  /**
934
868
  * Helper function to retry a promise with exponential backoff
935
869
  *