@limitless-exchange/sdk 0.0.3 → 1.0.1

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.
package/dist/index.mjs CHANGED
@@ -68,250 +68,92 @@ var WebSocketState = /* @__PURE__ */ ((WebSocketState2) => {
68
68
  return WebSocketState2;
69
69
  })(WebSocketState || {});
70
70
 
71
- // src/auth/signer.ts
72
- import { ethers } from "ethers";
73
- var MessageSigner = class {
74
- /**
75
- * Creates a new message signer instance.
76
- *
77
- * @param wallet - Ethers wallet instance for signing
78
- */
79
- constructor(wallet) {
80
- this.wallet = wallet;
81
- }
82
- /**
83
- * Creates authentication headers for API requests.
84
- *
85
- * @param signingMessage - Message to sign from the API
86
- * @returns Promise resolving to signature headers
87
- *
88
- * @example
89
- * ```typescript
90
- * const signer = new MessageSigner(wallet);
91
- * const headers = await signer.createAuthHeaders(message);
92
- * ```
93
- */
94
- async createAuthHeaders(signingMessage) {
95
- const hexMessage = this.stringToHex(signingMessage);
96
- const signature = await this.wallet.signMessage(signingMessage);
97
- const address = this.wallet.address;
98
- const recoveredAddress = ethers.verifyMessage(signingMessage, signature);
99
- if (address.toLowerCase() !== recoveredAddress.toLowerCase()) {
100
- throw new Error("Signature verification failed: address mismatch");
101
- }
102
- return {
103
- "x-account": address,
104
- "x-signing-message": hexMessage,
105
- "x-signature": signature
106
- };
107
- }
108
- /**
109
- * Signs EIP-712 typed data.
110
- *
111
- * @param domain - EIP-712 domain
112
- * @param types - EIP-712 types
113
- * @param value - Value to sign
114
- * @returns Promise resolving to signature string
115
- *
116
- * @example
117
- * ```typescript
118
- * const signature = await signer.signTypedData(domain, types, order);
119
- * ```
120
- */
121
- async signTypedData(domain, types, value) {
122
- return await this.wallet.signTypedData(domain, types, value);
123
- }
124
- /**
125
- * Gets the wallet address.
126
- *
127
- * @returns Ethereum address
128
- */
129
- getAddress() {
130
- return this.wallet.address;
131
- }
132
- /**
133
- * Converts a string to hex format.
134
- *
135
- * @param text - String to convert
136
- * @returns Hex string with 0x prefix
137
- * @internal
138
- */
139
- stringToHex(text) {
140
- return ethers.hexlify(ethers.toUtf8Bytes(text));
141
- }
142
- };
143
-
144
- // src/auth/authenticator.ts
145
- var Authenticator = class {
71
+ // src/types/market-class.ts
72
+ var Market = class _Market {
146
73
  /**
147
- * Creates a new authenticator instance.
148
- *
149
- * @param httpClient - HTTP client for API requests
150
- * @param signer - Message signer for wallet operations
151
- * @param logger - Optional logger for debugging and monitoring (default: no logging)
74
+ * Creates a Market instance.
152
75
  *
153
- * @example
154
- * ```typescript
155
- * // Without logging (default)
156
- * const authenticator = new Authenticator(httpClient, signer);
157
- *
158
- * // With logging
159
- * import { ConsoleLogger } from '@limitless-exchange/sdk';
160
- * const logger = new ConsoleLogger('debug');
161
- * const authenticator = new Authenticator(httpClient, signer, logger);
162
- * ```
76
+ * @param data - Market data from API
77
+ * @param httpClient - HTTP client for making requests
163
78
  */
164
- constructor(httpClient, signer, logger) {
79
+ constructor(data, httpClient) {
80
+ Object.assign(this, data);
165
81
  this.httpClient = httpClient;
166
- this.signer = signer;
167
- this.logger = logger || new NoOpLogger();
82
+ if (data.markets && Array.isArray(data.markets)) {
83
+ this.markets = data.markets.map((m) => new _Market(m, httpClient));
84
+ }
168
85
  }
169
86
  /**
170
- * Gets a signing message from the API.
171
- *
172
- * @returns Promise resolving to signing message string
173
- * @throws Error if API request fails
87
+ * Get user's orders for this market.
174
88
  *
175
- * @example
176
- * ```typescript
177
- * const message = await authenticator.getSigningMessage();
178
- * console.log(message);
179
- * ```
180
- */
181
- async getSigningMessage() {
182
- this.logger.debug("Requesting signing message from API");
183
- const message = await this.httpClient.get("/auth/signing-message");
184
- this.logger.debug("Received signing message", { length: message.length });
185
- return message;
186
- }
187
- /**
188
- * Authenticates with the API and obtains session cookie.
89
+ * @remarks
90
+ * Fetches all orders placed by the authenticated user for this specific market.
91
+ * Uses the http_client from the MarketFetcher that created this Market instance.
189
92
  *
190
- * @param options - Login options including client type and smart wallet
191
- * @returns Promise resolving to authentication result
192
- * @throws Error if authentication fails or smart wallet is required but not provided
93
+ * @returns Promise resolving to array of user orders
94
+ * @throws Error if market wasn't fetched via MarketFetcher
193
95
  *
194
96
  * @example
195
97
  * ```typescript
196
- * // EOA authentication
197
- * const result = await authenticator.authenticate({ client: 'eoa' });
198
- *
199
- * // ETHERSPOT with smart wallet
200
- * const result = await authenticator.authenticate({
201
- * client: 'etherspot',
202
- * smartWallet: '0x...'
203
- * });
98
+ * // Clean fluent API
99
+ * const market = await marketFetcher.getMarket("bitcoin-2024");
100
+ * const orders = await market.getUserOrders();
101
+ * console.log(`You have ${orders.length} orders in ${market.title}`);
204
102
  * ```
205
103
  */
206
- async authenticate(options = {}) {
207
- const client = options.client || "eoa";
208
- this.logger.info("Starting authentication", {
209
- client,
210
- hasSmartWallet: !!options.smartWallet
211
- });
212
- if (client === "etherspot" && !options.smartWallet) {
213
- this.logger.error("Smart wallet address required for ETHERSPOT client");
214
- throw new Error("Smart wallet address is required for ETHERSPOT client");
215
- }
216
- try {
217
- const signingMessage = await this.getSigningMessage();
218
- this.logger.debug("Creating signature headers");
219
- const headers = await this.signer.createAuthHeaders(signingMessage);
220
- this.logger.debug("Sending authentication request", { client });
221
- const response = await this.httpClient.postWithResponse(
222
- "/auth/login",
223
- { client, smartWallet: options.smartWallet },
224
- {
225
- headers,
226
- validateStatus: (status) => status < 500
227
- }
104
+ async getUserOrders() {
105
+ if (!this.httpClient) {
106
+ throw new Error(
107
+ "This Market instance has no httpClient attached. Make sure to fetch the market via MarketFetcher.getMarket() to use this method."
228
108
  );
229
- this.logger.debug("Extracting session cookie from response");
230
- const cookies = this.httpClient.extractCookies(response);
231
- const sessionCookie = cookies["limitless_session"];
232
- if (!sessionCookie) {
233
- this.logger.error("Session cookie not found in response headers");
234
- throw new Error("Failed to obtain session cookie from response");
235
- }
236
- this.httpClient.setSessionCookie(sessionCookie);
237
- this.logger.info("Authentication successful", {
238
- account: response.data.account,
239
- client: response.data.client
240
- });
241
- return {
242
- sessionCookie,
243
- profile: response.data
244
- };
245
- } catch (error) {
246
- this.logger.error("Authentication failed", error, {
247
- client
248
- });
249
- throw error;
250
- }
251
- }
252
- /**
253
- * Verifies the current authentication status.
254
- *
255
- * @param sessionCookie - Session cookie to verify
256
- * @returns Promise resolving to user's Ethereum address
257
- * @throws Error if session is invalid
258
- *
259
- * @example
260
- * ```typescript
261
- * const address = await authenticator.verifyAuth(sessionCookie);
262
- * console.log(`Authenticated as: ${address}`);
263
- * ```
264
- */
265
- async verifyAuth(sessionCookie) {
266
- this.logger.debug("Verifying authentication session");
267
- const originalCookie = this.httpClient["sessionCookie"];
268
- this.httpClient.setSessionCookie(sessionCookie);
269
- try {
270
- const address = await this.httpClient.get("/auth/verify-auth");
271
- this.logger.info("Session verified", { address });
272
- return address;
273
- } catch (error) {
274
- this.logger.error("Session verification failed", error);
275
- throw error;
276
- } finally {
277
- if (originalCookie) {
278
- this.httpClient.setSessionCookie(originalCookie);
279
- } else {
280
- this.httpClient.clearSessionCookie();
281
- }
282
109
  }
110
+ const response = await this.httpClient.get(`/markets/${this.slug}/user-orders`);
111
+ const orders = Array.isArray(response) ? response : response.orders || [];
112
+ return orders;
283
113
  }
284
- /**
285
- * Logs out and clears the session.
286
- *
287
- * @param sessionCookie - Session cookie to invalidate
288
- * @throws Error if logout request fails
289
- *
290
- * @example
291
- * ```typescript
292
- * await authenticator.logout(sessionCookie);
293
- * console.log('Logged out successfully');
294
- * ```
295
- */
296
- async logout(sessionCookie) {
297
- this.logger.debug("Logging out session");
298
- const originalCookie = this.httpClient["sessionCookie"];
299
- this.httpClient.setSessionCookie(sessionCookie);
300
- try {
301
- await this.httpClient.post("/auth/logout", {});
302
- this.logger.info("Logout successful");
303
- } catch (error) {
304
- this.logger.error("Logout failed", error);
305
- throw error;
306
- } finally {
307
- if (originalCookie) {
308
- this.httpClient.setSessionCookie(originalCookie);
309
- } else {
310
- this.httpClient.clearSessionCookie();
311
- }
312
- }
114
+ };
115
+
116
+ // src/api/http.ts
117
+ import axios from "axios";
118
+ import http from "http";
119
+ import https from "https";
120
+
121
+ // src/utils/constants.ts
122
+ var DEFAULT_API_URL = "https://api.limitless.exchange";
123
+ var DEFAULT_WS_URL = "wss://ws.limitless.exchange";
124
+ var DEFAULT_CHAIN_ID = 8453;
125
+ var BASE_SEPOLIA_CHAIN_ID = 84532;
126
+ var SIGNING_MESSAGE_TEMPLATE = "Welcome to Limitless.exchange! Please sign this message to verify your identity.\n\nNonce: {NONCE}";
127
+ var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
128
+ var CONTRACT_ADDRESSES = {
129
+ // Base mainnet (chainId: 8453)
130
+ 8453: {
131
+ USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
132
+ // Native USDC on Base
133
+ CTF: "0xC9c98965297Bc527861c898329Ee280632B76e18"
134
+ // Conditional Token Framework
135
+ },
136
+ // Base Sepolia testnet (chainId: 84532)
137
+ 84532: {
138
+ USDC: "0x...",
139
+ CTF: "0x..."
313
140
  }
314
141
  };
142
+ function getContractAddress(contractType, chainId = DEFAULT_CHAIN_ID) {
143
+ const addresses = CONTRACT_ADDRESSES[chainId];
144
+ if (!addresses) {
145
+ throw new Error(
146
+ `No contract addresses configured for chainId ${chainId}. Supported chains: ${Object.keys(CONTRACT_ADDRESSES).join(", ")}`
147
+ );
148
+ }
149
+ const address = addresses[contractType];
150
+ if (!address || address === "0x...") {
151
+ throw new Error(
152
+ `Contract address for ${contractType} not available on chainId ${chainId}. Please configure the address in constants.ts or use environment variables.`
153
+ );
154
+ }
155
+ return address;
156
+ }
315
157
 
316
158
  // src/api/errors.ts
317
159
  var APIError = class _APIError extends Error {
@@ -357,118 +199,33 @@ var APIError = class _APIError extends Error {
357
199
  return this.status === 401 || this.status === 403;
358
200
  }
359
201
  };
360
-
361
- // src/auth/authenticated-client.ts
362
- var AuthenticatedClient = class {
363
- /**
364
- * Creates a new authenticated client with auto-retry capability.
365
- *
366
- * @param config - Configuration for authenticated client
367
- */
368
- constructor(config) {
369
- this.httpClient = config.httpClient;
370
- this.authenticator = config.authenticator;
371
- this.client = config.client;
372
- this.smartWallet = config.smartWallet;
373
- this.logger = config.logger || new NoOpLogger();
374
- this.maxRetries = config.maxRetries ?? 1;
375
- }
376
- /**
377
- * Executes a function with automatic retry on authentication errors.
378
- *
379
- * @param fn - Function to execute with auth retry
380
- * @returns Promise resolving to the function result
381
- * @throws Error if max retries exceeded or non-auth error occurs
382
- *
383
- * @example
384
- * ```typescript
385
- * // Automatic retry on 401/403
386
- * const positions = await authClient.withRetry(() =>
387
- * portfolioFetcher.getPositions()
388
- * );
389
- *
390
- * // Works with any async operation
391
- * const order = await authClient.withRetry(() =>
392
- * orderClient.createOrder({ ... })
393
- * );
394
- * ```
395
- */
396
- async withRetry(fn) {
397
- let attempts = 0;
398
- while (attempts <= this.maxRetries) {
399
- try {
400
- return await fn();
401
- } catch (error) {
402
- if (error instanceof APIError && error.isAuthError() && attempts < this.maxRetries) {
403
- this.logger.info("Authentication expired, re-authenticating...", {
404
- attempt: attempts + 1,
405
- maxRetries: this.maxRetries
406
- });
407
- await this.reauthenticate();
408
- attempts++;
409
- continue;
410
- }
411
- throw error;
412
- }
202
+ var RateLimitError = class _RateLimitError extends APIError {
203
+ constructor(message = "Rate limit exceeded", status = 429, data = null, url, method) {
204
+ super(message, status, data, url, method);
205
+ this.name = "RateLimitError";
206
+ if (Error.captureStackTrace) {
207
+ Error.captureStackTrace(this, _RateLimitError);
413
208
  }
414
- throw new Error("Unexpected error: exceeded max retries");
415
- }
416
- /**
417
- * Re-authenticates with the API.
418
- *
419
- * @internal
420
- */
421
- async reauthenticate() {
422
- this.logger.debug("Re-authenticating with API");
423
- await this.authenticator.authenticate({
424
- client: this.client,
425
- smartWallet: this.smartWallet
426
- });
427
- this.logger.info("Re-authentication successful");
428
209
  }
429
210
  };
430
-
431
- // src/api/http.ts
432
- import axios from "axios";
433
- import http from "http";
434
- import https from "https";
435
-
436
- // src/utils/constants.ts
437
- var DEFAULT_API_URL = "https://api.limitless.exchange";
438
- var DEFAULT_WS_URL = "wss://ws.limitless.exchange";
439
- var DEFAULT_CHAIN_ID = 8453;
440
- var BASE_SEPOLIA_CHAIN_ID = 84532;
441
- var SIGNING_MESSAGE_TEMPLATE = "Welcome to Limitless.exchange! Please sign this message to verify your identity.\n\nNonce: {NONCE}";
442
- var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
443
- var CONTRACT_ADDRESSES = {
444
- // Base mainnet (chainId: 8453)
445
- 8453: {
446
- USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
447
- // Native USDC on Base
448
- CTF: "0xC9c98965297Bc527861c898329Ee280632B76e18"
449
- // Conditional Token Framework
450
- },
451
- // Base Sepolia testnet (chainId: 84532)
452
- 84532: {
453
- USDC: "0x...",
454
- CTF: "0x..."
211
+ var AuthenticationError = class _AuthenticationError extends APIError {
212
+ constructor(message = "Authentication failed", status = 401, data = null, url, method) {
213
+ super(message, status, data, url, method);
214
+ this.name = "AuthenticationError";
215
+ if (Error.captureStackTrace) {
216
+ Error.captureStackTrace(this, _AuthenticationError);
217
+ }
455
218
  }
456
219
  };
457
- function getContractAddress(contractType, chainId = DEFAULT_CHAIN_ID) {
458
- const addresses = CONTRACT_ADDRESSES[chainId];
459
- if (!addresses) {
460
- throw new Error(
461
- `No contract addresses configured for chainId ${chainId}. Supported chains: ${Object.keys(CONTRACT_ADDRESSES).join(", ")}`
462
- );
463
- }
464
- const address = addresses[contractType];
465
- if (!address || address === "0x...") {
466
- throw new Error(
467
- `Contract address for ${contractType} not available on chainId ${chainId}. Please configure the address in constants.ts or use environment variables.`
468
- );
220
+ var ValidationError = class _ValidationError extends APIError {
221
+ constructor(message, status = 400, data = null, url, method) {
222
+ super(message, status, data, url, method);
223
+ this.name = "ValidationError";
224
+ if (Error.captureStackTrace) {
225
+ Error.captureStackTrace(this, _ValidationError);
226
+ }
469
227
  }
470
- return address;
471
- }
228
+ };
472
229
 
473
230
  // src/api/http.ts
474
231
  var HttpClient = class {
@@ -478,8 +235,13 @@ var HttpClient = class {
478
235
  * @param config - Configuration options for the HTTP client
479
236
  */
480
237
  constructor(config = {}) {
481
- this.sessionCookie = config.sessionCookie;
238
+ this.apiKey = config.apiKey || process.env.LIMITLESS_API_KEY;
482
239
  this.logger = config.logger || new NoOpLogger();
240
+ if (!this.apiKey) {
241
+ this.logger.warn(
242
+ "API key not set. Authenticated endpoints will fail. Set LIMITLESS_API_KEY environment variable or pass apiKey parameter."
243
+ );
244
+ }
483
245
  const keepAlive = config.keepAlive !== false;
484
246
  const maxSockets = config.maxSockets || 50;
485
247
  const maxFreeSockets = config.maxFreeSockets || 10;
@@ -522,13 +284,17 @@ var HttpClient = class {
522
284
  setupInterceptors() {
523
285
  this.client.interceptors.request.use(
524
286
  (config) => {
525
- if (this.sessionCookie) {
526
- config.headers["Cookie"] = `limitless_session=${this.sessionCookie}`;
287
+ if (this.apiKey) {
288
+ config.headers["X-API-Key"] = this.apiKey;
527
289
  }
528
290
  const fullUrl = `${config.baseURL || ""}${config.url || ""}`;
529
291
  const method = config.method?.toUpperCase() || "GET";
292
+ const logHeaders = { ...config.headers };
293
+ if (logHeaders["X-API-Key"]) {
294
+ logHeaders["X-API-Key"] = "***";
295
+ }
530
296
  this.logger.debug(`\u2192 ${method} ${fullUrl}`, {
531
- headers: config.headers,
297
+ headers: logHeaders,
532
298
  body: config.data
533
299
  });
534
300
  return config;
@@ -569,7 +335,15 @@ var HttpClient = class {
569
335
  message = String(data);
570
336
  }
571
337
  }
572
- throw new APIError(message, status, data, url, method);
338
+ if (status === 429) {
339
+ throw new RateLimitError(message, status, data, url, method);
340
+ } else if (status === 401 || status === 403) {
341
+ throw new AuthenticationError(message, status, data, url, method);
342
+ } else if (status === 400) {
343
+ throw new ValidationError(message, status, data, url, method);
344
+ } else {
345
+ throw new APIError(message, status, data, url, method);
346
+ }
573
347
  } else if (error.request) {
574
348
  throw new Error("No response received from API");
575
349
  } else {
@@ -579,18 +353,18 @@ var HttpClient = class {
579
353
  );
580
354
  }
581
355
  /**
582
- * Sets the session cookie for authenticated requests.
356
+ * Sets the API key for authenticated requests.
583
357
  *
584
- * @param cookie - Session cookie value
358
+ * @param apiKey - API key value
585
359
  */
586
- setSessionCookie(cookie) {
587
- this.sessionCookie = cookie;
360
+ setApiKey(apiKey) {
361
+ this.apiKey = apiKey;
588
362
  }
589
363
  /**
590
- * Clears the session cookie.
364
+ * Clears the API key.
591
365
  */
592
- clearSessionCookie() {
593
- this.sessionCookie = void 0;
366
+ clearApiKey() {
367
+ this.apiKey = void 0;
594
368
  }
595
369
  /**
596
370
  * Performs a GET request.
@@ -615,19 +389,6 @@ var HttpClient = class {
615
389
  const response = await this.client.post(url, data, config);
616
390
  return response.data;
617
391
  }
618
- /**
619
- * Performs a POST request and returns the full response object.
620
- * Useful when you need access to response headers (e.g., for cookie extraction).
621
- *
622
- * @param url - Request URL
623
- * @param data - Request body data
624
- * @param config - Additional request configuration
625
- * @returns Promise resolving to the full AxiosResponse object
626
- * @internal
627
- */
628
- async postWithResponse(url, data, config) {
629
- return await this.client.post(url, data, config);
630
- }
631
392
  /**
632
393
  * Performs a DELETE request.
633
394
  *
@@ -650,26 +411,6 @@ var HttpClient = class {
650
411
  const response = await this.client.delete(url, deleteConfig);
651
412
  return response.data;
652
413
  }
653
- /**
654
- * Extracts cookies from response headers.
655
- *
656
- * @param response - Axios response object
657
- * @returns Object containing parsed cookies
658
- * @internal
659
- */
660
- extractCookies(response) {
661
- const setCookie = response.headers["set-cookie"];
662
- if (!setCookie) return {};
663
- const cookies = {};
664
- const cookieStrings = Array.isArray(setCookie) ? setCookie : [setCookie];
665
- for (const cookieString of cookieStrings) {
666
- const parts = cookieString.split(";")[0].split("=");
667
- if (parts.length === 2) {
668
- cookies[parts[0].trim()] = parts[1].trim();
669
- }
670
- }
671
- return cookies;
672
- }
673
414
  };
674
415
 
675
416
  // src/api/retry.ts
@@ -854,7 +595,7 @@ var RetryableClient = class {
854
595
  };
855
596
 
856
597
  // src/orders/builder.ts
857
- import { ethers as ethers2 } from "ethers";
598
+ import { ethers } from "ethers";
858
599
  var ZERO_ADDRESS2 = "0x0000000000000000000000000000000000000000";
859
600
  var DEFAULT_PRICE_TICK = 1e-3;
860
601
  var OrderBuilder = class {
@@ -936,7 +677,7 @@ var OrderBuilder = class {
936
677
  * @internal
937
678
  */
938
679
  isFOKOrder(args) {
939
- return "amount" in args;
680
+ return "makerAmount" in args;
940
681
  }
941
682
  /**
942
683
  * Generates a unique salt using timestamp + nano-offset pattern.
@@ -1090,7 +831,7 @@ var OrderBuilder = class {
1090
831
  }
1091
832
  }
1092
833
  const amountFormatted = makerAmount.toFixed(DECIMALS);
1093
- const amountScaled = ethers2.parseUnits(amountFormatted, DECIMALS);
834
+ const amountScaled = ethers.parseUnits(amountFormatted, DECIMALS);
1094
835
  const collateralAmount = Number(amountScaled);
1095
836
  return {
1096
837
  makerAmount: collateralAmount,
@@ -1110,7 +851,7 @@ var OrderBuilder = class {
1110
851
  if (!args.tokenId || args.tokenId === "0") {
1111
852
  throw new Error("Invalid tokenId: tokenId is required.");
1112
853
  }
1113
- if (args.taker && !ethers2.isAddress(args.taker)) {
854
+ if (args.taker && !ethers.isAddress(args.taker)) {
1114
855
  throw new Error(`Invalid taker address: ${args.taker}`);
1115
856
  }
1116
857
  if (this.isFOKOrder(args)) {
@@ -1121,7 +862,7 @@ var OrderBuilder = class {
1121
862
  throw new Error(`Invalid makerAmount: ${args.makerAmount}. Maker amount must be positive.`);
1122
863
  }
1123
864
  } else {
1124
- if (args.price < 0 || args.price > 1) {
865
+ if (args.price == null || args.price < 0 || args.price > 1) {
1125
866
  throw new Error(`Invalid price: ${args.price}. Price must be between 0 and 1.`);
1126
867
  }
1127
868
  if (args.size <= 0) {
@@ -1210,11 +951,11 @@ var OrderSigner = class {
1210
951
  signatureType: order.signatureType
1211
952
  };
1212
953
  this.logger.debug("EIP-712 Order Value", orderValue);
1213
- console.log("[OrderSigner] Full signing payload:", JSON.stringify({
954
+ this.logger.debug("Full signing payload", {
1214
955
  domain,
1215
956
  types: this.getTypes(),
1216
957
  value: orderValue
1217
- }, null, 2));
958
+ });
1218
959
  try {
1219
960
  const signature = await this.wallet.signTypedData(domain, types, orderValue);
1220
961
  this.logger.info("Successfully generated EIP-712 signature", {
@@ -1274,11 +1015,11 @@ var OrderSigner = class {
1274
1015
  };
1275
1016
 
1276
1017
  // src/orders/validator.ts
1277
- import { ethers as ethers3 } from "ethers";
1278
- var ValidationError = class extends Error {
1018
+ import { ethers as ethers2 } from "ethers";
1019
+ var OrderValidationError = class extends Error {
1279
1020
  constructor(message) {
1280
1021
  super(message);
1281
- this.name = "ValidationError";
1022
+ this.name = "OrderValidationError";
1282
1023
  }
1283
1024
  };
1284
1025
  function isFOKOrder(args) {
@@ -1286,126 +1027,126 @@ function isFOKOrder(args) {
1286
1027
  }
1287
1028
  function validateOrderArgs(args) {
1288
1029
  if (!args.tokenId) {
1289
- throw new ValidationError("TokenId is required");
1030
+ throw new OrderValidationError("TokenId is required");
1290
1031
  }
1291
1032
  if (args.tokenId === "0") {
1292
- throw new ValidationError("TokenId cannot be zero");
1033
+ throw new OrderValidationError("TokenId cannot be zero");
1293
1034
  }
1294
1035
  if (!/^\d+$/.test(args.tokenId)) {
1295
- throw new ValidationError(`Invalid tokenId format: ${args.tokenId}`);
1036
+ throw new OrderValidationError(`Invalid tokenId format: ${args.tokenId}`);
1296
1037
  }
1297
- if (args.taker && !ethers3.isAddress(args.taker)) {
1298
- throw new ValidationError(`Invalid taker address: ${args.taker}`);
1038
+ if (args.taker && !ethers2.isAddress(args.taker)) {
1039
+ throw new OrderValidationError(`Invalid taker address: ${args.taker}`);
1299
1040
  }
1300
1041
  if (args.expiration !== void 0) {
1301
1042
  if (!/^\d+$/.test(args.expiration)) {
1302
- throw new ValidationError(`Invalid expiration format: ${args.expiration}`);
1043
+ throw new OrderValidationError(`Invalid expiration format: ${args.expiration}`);
1303
1044
  }
1304
1045
  }
1305
1046
  if (args.nonce !== void 0) {
1306
1047
  if (!Number.isInteger(args.nonce) || args.nonce < 0) {
1307
- throw new ValidationError(`Invalid nonce: ${args.nonce}`);
1048
+ throw new OrderValidationError(`Invalid nonce: ${args.nonce}`);
1308
1049
  }
1309
1050
  }
1310
1051
  if (isFOKOrder(args)) {
1311
1052
  if (typeof args.makerAmount !== "number" || isNaN(args.makerAmount)) {
1312
- throw new ValidationError("Amount must be a valid number");
1053
+ throw new OrderValidationError("Amount must be a valid number");
1313
1054
  }
1314
1055
  if (args.makerAmount <= 0) {
1315
- throw new ValidationError(`Amount must be positive, got: ${args.makerAmount}`);
1056
+ throw new OrderValidationError(`Amount must be positive, got: ${args.makerAmount}`);
1316
1057
  }
1317
1058
  const amountStr = args.makerAmount.toString();
1318
1059
  const decimalIndex = amountStr.indexOf(".");
1319
1060
  if (decimalIndex !== -1) {
1320
1061
  const decimalPlaces = amountStr.length - decimalIndex - 1;
1321
1062
  if (decimalPlaces > 2) {
1322
- throw new ValidationError(
1063
+ throw new OrderValidationError(
1323
1064
  `Amount must have max 2 decimal places, got: ${args.makerAmount} (${decimalPlaces} decimals)`
1324
1065
  );
1325
1066
  }
1326
1067
  }
1327
1068
  } else {
1328
1069
  if (typeof args.price !== "number" || isNaN(args.price)) {
1329
- throw new ValidationError("Price must be a valid number");
1070
+ throw new OrderValidationError("Price must be a valid number");
1330
1071
  }
1331
1072
  if (args.price < 0 || args.price > 1) {
1332
- throw new ValidationError(`Price must be between 0 and 1, got: ${args.price}`);
1073
+ throw new OrderValidationError(`Price must be between 0 and 1, got: ${args.price}`);
1333
1074
  }
1334
1075
  if (typeof args.size !== "number" || isNaN(args.size)) {
1335
- throw new ValidationError("Size must be a valid number");
1076
+ throw new OrderValidationError("Size must be a valid number");
1336
1077
  }
1337
1078
  if (args.size <= 0) {
1338
- throw new ValidationError(`Size must be positive, got: ${args.size}`);
1079
+ throw new OrderValidationError(`Size must be positive, got: ${args.size}`);
1339
1080
  }
1340
1081
  }
1341
1082
  }
1342
1083
  function validateUnsignedOrder(order) {
1343
- if (!ethers3.isAddress(order.maker)) {
1344
- throw new ValidationError(`Invalid maker address: ${order.maker}`);
1084
+ if (!ethers2.isAddress(order.maker)) {
1085
+ throw new OrderValidationError(`Invalid maker address: ${order.maker}`);
1345
1086
  }
1346
- if (!ethers3.isAddress(order.signer)) {
1347
- throw new ValidationError(`Invalid signer address: ${order.signer}`);
1087
+ if (!ethers2.isAddress(order.signer)) {
1088
+ throw new OrderValidationError(`Invalid signer address: ${order.signer}`);
1348
1089
  }
1349
- if (!ethers3.isAddress(order.taker)) {
1350
- throw new ValidationError(`Invalid taker address: ${order.taker}`);
1090
+ if (!ethers2.isAddress(order.taker)) {
1091
+ throw new OrderValidationError(`Invalid taker address: ${order.taker}`);
1351
1092
  }
1352
1093
  if (!order.makerAmount || order.makerAmount === 0) {
1353
- throw new ValidationError("MakerAmount must be greater than zero");
1094
+ throw new OrderValidationError("MakerAmount must be greater than zero");
1354
1095
  }
1355
1096
  if (!order.takerAmount || order.takerAmount === 0) {
1356
- throw new ValidationError("TakerAmount must be greater than zero");
1097
+ throw new OrderValidationError("TakerAmount must be greater than zero");
1357
1098
  }
1358
1099
  if (typeof order.makerAmount !== "number" || order.makerAmount <= 0) {
1359
- throw new ValidationError(`Invalid makerAmount: ${order.makerAmount}`);
1100
+ throw new OrderValidationError(`Invalid makerAmount: ${order.makerAmount}`);
1360
1101
  }
1361
1102
  if (typeof order.takerAmount !== "number" || order.takerAmount <= 0) {
1362
- throw new ValidationError(`Invalid takerAmount: ${order.takerAmount}`);
1103
+ throw new OrderValidationError(`Invalid takerAmount: ${order.takerAmount}`);
1363
1104
  }
1364
1105
  if (!/^\d+$/.test(order.tokenId)) {
1365
- throw new ValidationError(`Invalid tokenId format: ${order.tokenId}`);
1106
+ throw new OrderValidationError(`Invalid tokenId format: ${order.tokenId}`);
1366
1107
  }
1367
1108
  if (!/^\d+$/.test(order.expiration)) {
1368
- throw new ValidationError(`Invalid expiration format: ${order.expiration}`);
1109
+ throw new OrderValidationError(`Invalid expiration format: ${order.expiration}`);
1369
1110
  }
1370
1111
  if (!Number.isInteger(order.salt) || order.salt <= 0) {
1371
- throw new ValidationError(`Invalid salt: ${order.salt}`);
1112
+ throw new OrderValidationError(`Invalid salt: ${order.salt}`);
1372
1113
  }
1373
1114
  if (!Number.isInteger(order.nonce) || order.nonce < 0) {
1374
- throw new ValidationError(`Invalid nonce: ${order.nonce}`);
1115
+ throw new OrderValidationError(`Invalid nonce: ${order.nonce}`);
1375
1116
  }
1376
1117
  if (!Number.isInteger(order.feeRateBps) || order.feeRateBps < 0) {
1377
- throw new ValidationError(`Invalid feeRateBps: ${order.feeRateBps}`);
1118
+ throw new OrderValidationError(`Invalid feeRateBps: ${order.feeRateBps}`);
1378
1119
  }
1379
1120
  if (order.side !== 0 && order.side !== 1) {
1380
- throw new ValidationError(`Invalid side: ${order.side}. Must be 0 (BUY) or 1 (SELL)`);
1121
+ throw new OrderValidationError(`Invalid side: ${order.side}. Must be 0 (BUY) or 1 (SELL)`);
1381
1122
  }
1382
1123
  if (!Number.isInteger(order.signatureType) || order.signatureType < 0) {
1383
- throw new ValidationError(`Invalid signatureType: ${order.signatureType}`);
1124
+ throw new OrderValidationError(`Invalid signatureType: ${order.signatureType}`);
1384
1125
  }
1385
1126
  if (order.price !== void 0) {
1386
1127
  if (typeof order.price !== "number" || isNaN(order.price)) {
1387
- throw new ValidationError("Price must be a valid number");
1128
+ throw new OrderValidationError("Price must be a valid number");
1388
1129
  }
1389
1130
  if (order.price < 0 || order.price > 1) {
1390
- throw new ValidationError(`Price must be between 0 and 1, got: ${order.price}`);
1131
+ throw new OrderValidationError(`Price must be between 0 and 1, got: ${order.price}`);
1391
1132
  }
1392
1133
  }
1393
1134
  }
1394
1135
  function validateSignedOrder(order) {
1395
1136
  validateUnsignedOrder(order);
1396
1137
  if (!order.signature) {
1397
- throw new ValidationError("Signature is required");
1138
+ throw new OrderValidationError("Signature is required");
1398
1139
  }
1399
1140
  if (!order.signature.startsWith("0x")) {
1400
- throw new ValidationError("Signature must start with 0x");
1141
+ throw new OrderValidationError("Signature must start with 0x");
1401
1142
  }
1402
1143
  if (order.signature.length !== 132) {
1403
- throw new ValidationError(
1144
+ throw new OrderValidationError(
1404
1145
  `Invalid signature length: ${order.signature.length}. Expected 132 characters.`
1405
1146
  );
1406
1147
  }
1407
1148
  if (!/^0x[0-9a-fA-F]{130}$/.test(order.signature)) {
1408
- throw new ValidationError("Signature must be valid hex string");
1149
+ throw new OrderValidationError("Signature must be valid hex string");
1409
1150
  }
1410
1151
  }
1411
1152
 
@@ -1467,13 +1208,18 @@ var MarketFetcher = class {
1467
1208
  this.logger.debug("Fetching active markets", { params });
1468
1209
  try {
1469
1210
  const response = await this.httpClient.get(endpoint);
1211
+ const markets = response.data.map((marketData) => new Market(marketData, this.httpClient));
1212
+ const result = {
1213
+ data: markets,
1214
+ totalMarketsCount: response.totalMarketsCount
1215
+ };
1470
1216
  this.logger.info("Active markets fetched successfully", {
1471
- count: response.data.length,
1217
+ count: markets.length,
1472
1218
  total: response.totalMarketsCount,
1473
1219
  sortBy: params?.sortBy,
1474
1220
  page: params?.page
1475
1221
  });
1476
- return response;
1222
+ return result;
1477
1223
  } catch (error) {
1478
1224
  this.logger.error("Failed to fetch active markets", error, { params });
1479
1225
  throw error;
@@ -1493,9 +1239,14 @@ var MarketFetcher = class {
1493
1239
  *
1494
1240
  * @example
1495
1241
  * ```typescript
1242
+ * // Get market
1496
1243
  * const market = await fetcher.getMarket('bitcoin-price-2024');
1497
1244
  * console.log(`Market: ${market.title}`);
1498
1245
  *
1246
+ * // Fluent API - get user orders for this market (clean!)
1247
+ * const orders = await market.getUserOrders();
1248
+ * console.log(`You have ${orders.length} orders`);
1249
+ *
1499
1250
  * // Venue is now cached for order signing
1500
1251
  * await orderClient.createOrder({
1501
1252
  * marketSlug: 'bitcoin-price-2024',
@@ -1506,7 +1257,8 @@ var MarketFetcher = class {
1506
1257
  async getMarket(slug) {
1507
1258
  this.logger.debug("Fetching market", { slug });
1508
1259
  try {
1509
- const market = await this.httpClient.get(`/markets/${slug}`);
1260
+ const response = await this.httpClient.get(`/markets/${slug}`);
1261
+ const market = new Market(response, this.httpClient);
1510
1262
  if (market.venue) {
1511
1263
  this.venueCache.set(slug, market.venue);
1512
1264
  this.logger.debug("Venue cached for order signing", {
@@ -1589,30 +1341,164 @@ var MarketFetcher = class {
1589
1341
  throw error;
1590
1342
  }
1591
1343
  }
1344
+ };
1345
+
1346
+ // src/portfolio/fetcher.ts
1347
+ var PortfolioFetcher = class {
1592
1348
  /**
1593
- * Gets the current price for a token.
1349
+ * Creates a new portfolio fetcher instance.
1594
1350
  *
1595
- * @param tokenId - Token ID
1596
- * @returns Promise resolving to price information
1597
- * @throws Error if API request fails
1351
+ * @param httpClient - Authenticated HTTP client for API requests
1352
+ * @param logger - Optional logger for debugging (default: no logging)
1598
1353
  *
1599
1354
  * @example
1600
1355
  * ```typescript
1601
- * const price = await fetcher.getPrice('123456');
1602
- * console.log(`Current price: ${price.price}`);
1356
+ * // Create authenticated client
1357
+ * const httpClient = new HttpClient({ baseURL: API_URL });
1358
+ * await authenticator.authenticate({ client: 'eoa' });
1359
+ *
1360
+ * // Create portfolio fetcher
1361
+ * const portfolioFetcher = new PortfolioFetcher(httpClient);
1603
1362
  * ```
1604
1363
  */
1605
- async getPrice(tokenId) {
1606
- this.logger.debug("Fetching price", { tokenId });
1607
- try {
1608
- const price = await this.httpClient.get(`/prices/${tokenId}`);
1609
- this.logger.info("Price fetched successfully", {
1610
- tokenId,
1611
- price: price.price
1612
- });
1613
- return price;
1364
+ constructor(httpClient, logger) {
1365
+ this.httpClient = httpClient;
1366
+ this.logger = logger || new NoOpLogger();
1367
+ }
1368
+ /**
1369
+ * Gets user profile for a specific wallet address.
1370
+ *
1371
+ * @remarks
1372
+ * Returns user profile data including user ID and fee rate.
1373
+ * Used internally by OrderClient to fetch user data.
1374
+ *
1375
+ * @param address - Wallet address to fetch profile for
1376
+ * @returns Promise resolving to user profile data
1377
+ * @throws Error if API request fails or user is not authenticated
1378
+ *
1379
+ * @example
1380
+ * ```typescript
1381
+ * const profile = await portfolioFetcher.getProfile('0x1234...');
1382
+ * console.log(`User ID: ${profile.id}`);
1383
+ * console.log(`Account: ${profile.account}`);
1384
+ * console.log(`Fee Rate: ${profile.rank?.feeRateBps}`);
1385
+ * ```
1386
+ */
1387
+ async getProfile(address) {
1388
+ this.logger.debug("Fetching user profile", { address });
1389
+ try {
1390
+ const response = await this.httpClient.get(`/profiles/${address}`);
1391
+ this.logger.info("User profile fetched successfully", { address });
1392
+ return response;
1614
1393
  } catch (error) {
1615
- this.logger.error("Failed to fetch price", error, { tokenId });
1394
+ this.logger.error("Failed to fetch user profile", error, { address });
1395
+ throw error;
1396
+ }
1397
+ }
1398
+ /**
1399
+ * Gets raw portfolio positions response from API.
1400
+ *
1401
+ * @returns Promise resolving to portfolio positions response with CLOB and AMM positions
1402
+ * @throws Error if API request fails or user is not authenticated
1403
+ *
1404
+ * @example
1405
+ * ```typescript
1406
+ * const response = await portfolioFetcher.getPositions();
1407
+ * console.log(`CLOB positions: ${response.clob.length}`);
1408
+ * console.log(`AMM positions: ${response.amm.length}`);
1409
+ * console.log(`Total points: ${response.accumulativePoints}`);
1410
+ * ```
1411
+ */
1412
+ async getPositions() {
1413
+ this.logger.debug("Fetching user positions");
1414
+ try {
1415
+ const response = await this.httpClient.get("/portfolio/positions");
1416
+ this.logger.info("Positions fetched successfully", {
1417
+ clobCount: response.clob?.length || 0,
1418
+ ammCount: response.amm?.length || 0
1419
+ });
1420
+ return response;
1421
+ } catch (error) {
1422
+ this.logger.error("Failed to fetch positions", error);
1423
+ throw error;
1424
+ }
1425
+ }
1426
+ /**
1427
+ * Gets CLOB positions only.
1428
+ *
1429
+ * @returns Promise resolving to array of CLOB positions
1430
+ * @throws Error if API request fails
1431
+ *
1432
+ * @example
1433
+ * ```typescript
1434
+ * const clobPositions = await portfolioFetcher.getCLOBPositions();
1435
+ * clobPositions.forEach(pos => {
1436
+ * console.log(`${pos.market.title}: YES ${pos.positions.yes.unrealizedPnl} P&L`);
1437
+ * });
1438
+ * ```
1439
+ */
1440
+ async getCLOBPositions() {
1441
+ const response = await this.getPositions();
1442
+ return response.clob || [];
1443
+ }
1444
+ /**
1445
+ * Gets AMM positions only.
1446
+ *
1447
+ * @returns Promise resolving to array of AMM positions
1448
+ * @throws Error if API request fails
1449
+ *
1450
+ * @example
1451
+ * ```typescript
1452
+ * const ammPositions = await portfolioFetcher.getAMMPositions();
1453
+ * ammPositions.forEach(pos => {
1454
+ * console.log(`${pos.market.title}: ${pos.unrealizedPnl} P&L`);
1455
+ * });
1456
+ * ```
1457
+ */
1458
+ async getAMMPositions() {
1459
+ const response = await this.getPositions();
1460
+ return response.amm || [];
1461
+ }
1462
+ /**
1463
+ * Gets paginated history of user actions.
1464
+ *
1465
+ * Includes AMM trades, CLOB trades, Negrisk trades & conversions.
1466
+ *
1467
+ * @param page - Page number (starts at 1)
1468
+ * @param limit - Number of items per page
1469
+ * @returns Promise resolving to paginated history response
1470
+ * @throws Error if API request fails or user is not authenticated
1471
+ *
1472
+ * @example
1473
+ * ```typescript
1474
+ * // Get first page
1475
+ * const response = await portfolioFetcher.getUserHistory(1, 20);
1476
+ * console.log(`Found ${response.data.length} of ${response.totalCount} entries`);
1477
+ *
1478
+ * // Process history entries
1479
+ * for (const entry of response.data) {
1480
+ * console.log(`Type: ${entry.type}`);
1481
+ * console.log(`Market: ${entry.marketSlug}`);
1482
+ * }
1483
+ *
1484
+ * // Get next page
1485
+ * const page2 = await portfolioFetcher.getUserHistory(2, 20);
1486
+ * ```
1487
+ */
1488
+ async getUserHistory(page = 1, limit = 10) {
1489
+ this.logger.debug("Fetching user history", { page, limit });
1490
+ try {
1491
+ const params = new URLSearchParams({
1492
+ page: page.toString(),
1493
+ limit: limit.toString()
1494
+ });
1495
+ const response = await this.httpClient.get(
1496
+ `/portfolio/history?${params.toString()}`
1497
+ );
1498
+ this.logger.info("User history fetched successfully");
1499
+ return response;
1500
+ } catch (error) {
1501
+ this.logger.error("Failed to fetch user history", error, { page, limit });
1616
1502
  throw error;
1617
1503
  }
1618
1504
  }
@@ -1627,10 +1513,8 @@ var OrderClient = class {
1627
1513
  */
1628
1514
  constructor(config) {
1629
1515
  this.httpClient = config.httpClient;
1516
+ this.wallet = config.wallet;
1630
1517
  this.logger = config.logger || new NoOpLogger();
1631
- this.ownerId = config.userData.userId;
1632
- const feeRateBps = config.userData.feeRateBps;
1633
- this.orderBuilder = new OrderBuilder(config.wallet.address, feeRateBps, 1e-3);
1634
1518
  this.orderSigner = new OrderSigner(config.wallet, this.logger);
1635
1519
  this.marketFetcher = config.marketFetcher || new MarketFetcher(config.httpClient, this.logger);
1636
1520
  if (config.signingConfig) {
@@ -1647,6 +1531,37 @@ var OrderClient = class {
1647
1531
  });
1648
1532
  }
1649
1533
  }
1534
+ /**
1535
+ * Ensures user data is loaded and cached.
1536
+ * Fetches from profile API on first call, then caches for subsequent calls.
1537
+ *
1538
+ * @returns Promise resolving to cached user data
1539
+ * @internal
1540
+ */
1541
+ async ensureUserData() {
1542
+ if (!this.cachedUserData) {
1543
+ this.logger.info("Fetching user profile for order client initialization...", {
1544
+ walletAddress: this.wallet.address
1545
+ });
1546
+ const portfolioFetcher = new PortfolioFetcher(this.httpClient);
1547
+ const profile = await portfolioFetcher.getProfile(this.wallet.address);
1548
+ this.cachedUserData = {
1549
+ userId: profile.id,
1550
+ feeRateBps: profile.rank?.feeRateBps || 300
1551
+ };
1552
+ this.orderBuilder = new OrderBuilder(
1553
+ this.wallet.address,
1554
+ this.cachedUserData.feeRateBps,
1555
+ 1e-3
1556
+ );
1557
+ this.logger.info("Order Client initialized", {
1558
+ walletAddress: profile.account,
1559
+ userId: this.cachedUserData.userId,
1560
+ feeRate: `${this.cachedUserData.feeRateBps / 100}%`
1561
+ });
1562
+ }
1563
+ return this.cachedUserData;
1564
+ }
1650
1565
  /**
1651
1566
  * Creates and submits a new order.
1652
1567
  *
@@ -1683,6 +1598,7 @@ var OrderClient = class {
1683
1598
  * ```
1684
1599
  */
1685
1600
  async createOrder(params) {
1601
+ const userData = await this.ensureUserData();
1686
1602
  this.logger.info("Creating order", {
1687
1603
  side: params.side,
1688
1604
  orderType: params.orderType,
@@ -1717,10 +1633,7 @@ var OrderClient = class {
1717
1633
  makerAmount: unsignedOrder.makerAmount,
1718
1634
  takerAmount: unsignedOrder.takerAmount
1719
1635
  });
1720
- const signature = await this.orderSigner.signOrder(
1721
- unsignedOrder,
1722
- dynamicSigningConfig
1723
- );
1636
+ const signature = await this.orderSigner.signOrder(unsignedOrder, dynamicSigningConfig);
1724
1637
  const payload = {
1725
1638
  order: {
1726
1639
  ...unsignedOrder,
@@ -1728,14 +1641,10 @@ var OrderClient = class {
1728
1641
  },
1729
1642
  orderType: params.orderType,
1730
1643
  marketSlug: params.marketSlug,
1731
- ownerId: this.ownerId
1644
+ ownerId: userData.userId
1732
1645
  };
1733
- this.logger.debug("Submitting order to API");
1734
- console.log("[OrderClient] Full API request payload:", JSON.stringify(payload, null, 2));
1735
- const apiResponse = await this.httpClient.post(
1736
- "/orders",
1737
- payload
1738
- );
1646
+ this.logger.debug("Submitting order to API", payload);
1647
+ const apiResponse = await this.httpClient.post("/orders", payload);
1739
1648
  this.logger.info("Order created successfully", {
1740
1649
  orderId: apiResponse.order.id
1741
1650
  });
@@ -1798,9 +1707,7 @@ var OrderClient = class {
1798
1707
  */
1799
1708
  async cancel(orderId) {
1800
1709
  this.logger.info("Cancelling order", { orderId });
1801
- const response = await this.httpClient.delete(
1802
- `/orders/${orderId}`
1803
- );
1710
+ const response = await this.httpClient.delete(`/orders/${orderId}`);
1804
1711
  this.logger.info("Order cancellation response", {
1805
1712
  orderId,
1806
1713
  message: response.message
@@ -1823,42 +1730,13 @@ var OrderClient = class {
1823
1730
  */
1824
1731
  async cancelAll(marketSlug) {
1825
1732
  this.logger.info("Cancelling all orders for market", { marketSlug });
1826
- const response = await this.httpClient.delete(
1827
- `/orders/all/${marketSlug}`
1828
- );
1733
+ const response = await this.httpClient.delete(`/orders/all/${marketSlug}`);
1829
1734
  this.logger.info("All orders cancellation response", {
1830
1735
  marketSlug,
1831
1736
  message: response.message
1832
1737
  });
1833
1738
  return response;
1834
1739
  }
1835
- /**
1836
- * @deprecated Use `cancel()` instead
1837
- */
1838
- async cancelOrder(orderId) {
1839
- await this.cancel(orderId);
1840
- }
1841
- /**
1842
- * Gets an order by ID.
1843
- *
1844
- * @param orderId - Order ID to fetch
1845
- * @returns Promise resolving to order details
1846
- *
1847
- * @throws Error if order not found
1848
- *
1849
- * @example
1850
- * ```typescript
1851
- * const order = await orderClient.getOrder('order-id-123');
1852
- * console.log(order.order.side);
1853
- * ```
1854
- */
1855
- async getOrder(orderId) {
1856
- this.logger.debug("Fetching order", { orderId });
1857
- const response = await this.httpClient.get(
1858
- `/orders/${orderId}`
1859
- );
1860
- return response;
1861
- }
1862
1740
  /**
1863
1741
  * Builds an unsigned order without submitting.
1864
1742
  *
@@ -1867,11 +1745,11 @@ var OrderClient = class {
1867
1745
  * before signing and submission.
1868
1746
  *
1869
1747
  * @param params - Order parameters
1870
- * @returns Unsigned order
1748
+ * @returns Promise resolving to unsigned order
1871
1749
  *
1872
1750
  * @example
1873
1751
  * ```typescript
1874
- * const unsignedOrder = orderClient.buildUnsignedOrder({
1752
+ * const unsignedOrder = await orderClient.buildUnsignedOrder({
1875
1753
  * tokenId: '123456',
1876
1754
  * price: 0.65,
1877
1755
  * size: 100,
@@ -1879,7 +1757,8 @@ var OrderClient = class {
1879
1757
  * });
1880
1758
  * ```
1881
1759
  */
1882
- buildUnsignedOrder(params) {
1760
+ async buildUnsignedOrder(params) {
1761
+ await this.ensureUserData();
1883
1762
  return this.orderBuilder.buildOrder(params);
1884
1763
  }
1885
1764
  /**
@@ -1900,308 +1779,35 @@ var OrderClient = class {
1900
1779
  async signOrder(order) {
1901
1780
  return await this.orderSigner.signOrder(order, this.signingConfig);
1902
1781
  }
1903
- };
1904
-
1905
- // src/portfolio/fetcher.ts
1906
- var PortfolioFetcher = class {
1907
- /**
1908
- * Creates a new portfolio fetcher instance.
1909
- *
1910
- * @param httpClient - Authenticated HTTP client for API requests
1911
- * @param logger - Optional logger for debugging (default: no logging)
1912
- *
1913
- * @example
1914
- * ```typescript
1915
- * // Create authenticated client
1916
- * const httpClient = new HttpClient({ baseURL: API_URL });
1917
- * await authenticator.authenticate({ client: 'eoa' });
1918
- *
1919
- * // Create portfolio fetcher
1920
- * const portfolioFetcher = new PortfolioFetcher(httpClient);
1921
- * ```
1922
- */
1923
- constructor(httpClient, logger) {
1924
- this.httpClient = httpClient;
1925
- this.logger = logger || new NoOpLogger();
1926
- }
1927
1782
  /**
1928
- * Gets raw portfolio positions response from API.
1929
- *
1930
- * @returns Promise resolving to portfolio positions response with CLOB and AMM positions
1931
- * @throws Error if API request fails or user is not authenticated
1932
- *
1933
- * @example
1934
- * ```typescript
1935
- * const response = await portfolioFetcher.getPositions();
1936
- * console.log(`CLOB positions: ${response.clob.length}`);
1937
- * console.log(`AMM positions: ${response.amm.length}`);
1938
- * console.log(`Total points: ${response.accumulativePoints}`);
1939
- * ```
1940
- */
1941
- async getPositions() {
1942
- this.logger.debug("Fetching user positions");
1943
- try {
1944
- const response = await this.httpClient.get(
1945
- "/portfolio/positions"
1946
- );
1947
- this.logger.info("Positions fetched successfully", {
1948
- clobCount: response.clob?.length || 0,
1949
- ammCount: response.amm?.length || 0
1950
- });
1951
- return response;
1952
- } catch (error) {
1953
- this.logger.error("Failed to fetch positions", error);
1954
- throw error;
1955
- }
1956
- }
1957
- /**
1958
- * Gets CLOB positions only.
1959
- *
1960
- * @returns Promise resolving to array of CLOB positions
1961
- * @throws Error if API request fails
1962
- *
1963
- * @example
1964
- * ```typescript
1965
- * const clobPositions = await portfolioFetcher.getCLOBPositions();
1966
- * clobPositions.forEach(pos => {
1967
- * console.log(`${pos.market.title}: YES ${pos.positions.yes.unrealizedPnl} P&L`);
1968
- * });
1969
- * ```
1970
- */
1971
- async getCLOBPositions() {
1972
- const response = await this.getPositions();
1973
- return response.clob || [];
1974
- }
1975
- /**
1976
- * Gets AMM positions only.
1977
- *
1978
- * @returns Promise resolving to array of AMM positions
1979
- * @throws Error if API request fails
1980
- *
1981
- * @example
1982
- * ```typescript
1983
- * const ammPositions = await portfolioFetcher.getAMMPositions();
1984
- * ammPositions.forEach(pos => {
1985
- * console.log(`${pos.market.title}: ${pos.unrealizedPnl} P&L`);
1986
- * });
1987
- * ```
1988
- */
1989
- async getAMMPositions() {
1990
- const response = await this.getPositions();
1991
- return response.amm || [];
1992
- }
1993
- /**
1994
- * Flattens positions into a unified format for easier consumption.
1995
- *
1996
- * @remarks
1997
- * Converts CLOB positions (which have YES/NO sides) and AMM positions
1998
- * into a unified Position array. Only includes positions with non-zero values.
1999
- *
2000
- * @returns Promise resolving to array of flattened positions
2001
- * @throws Error if API request fails
2002
- *
2003
- * @example
2004
- * ```typescript
2005
- * const positions = await portfolioFetcher.getFlattenedPositions();
2006
- * positions.forEach(pos => {
2007
- * const pnlPercent = (pos.unrealizedPnl / pos.costBasis) * 100;
2008
- * console.log(`${pos.market.title} (${pos.side}): ${pnlPercent.toFixed(2)}% P&L`);
2009
- * });
2010
- * ```
2011
- */
2012
- async getFlattenedPositions() {
2013
- const response = await this.getPositions();
2014
- const positions = [];
2015
- for (const clobPos of response.clob || []) {
2016
- const yesCost = parseFloat(clobPos.positions.yes.cost);
2017
- const yesValue = parseFloat(clobPos.positions.yes.marketValue);
2018
- if (yesCost > 0 || yesValue > 0) {
2019
- positions.push({
2020
- type: "CLOB",
2021
- market: clobPos.market,
2022
- side: "YES",
2023
- costBasis: yesCost,
2024
- marketValue: yesValue,
2025
- unrealizedPnl: parseFloat(clobPos.positions.yes.unrealizedPnl),
2026
- realizedPnl: parseFloat(clobPos.positions.yes.realisedPnl),
2027
- currentPrice: clobPos.latestTrade?.latestYesPrice ?? 0,
2028
- avgPrice: yesCost > 0 ? parseFloat(clobPos.positions.yes.fillPrice) / 1e6 : 0,
2029
- tokenBalance: parseFloat(clobPos.tokensBalance.yes)
2030
- });
2031
- }
2032
- const noCost = parseFloat(clobPos.positions.no.cost);
2033
- const noValue = parseFloat(clobPos.positions.no.marketValue);
2034
- if (noCost > 0 || noValue > 0) {
2035
- positions.push({
2036
- type: "CLOB",
2037
- market: clobPos.market,
2038
- side: "NO",
2039
- costBasis: noCost,
2040
- marketValue: noValue,
2041
- unrealizedPnl: parseFloat(clobPos.positions.no.unrealizedPnl),
2042
- realizedPnl: parseFloat(clobPos.positions.no.realisedPnl),
2043
- currentPrice: clobPos.latestTrade?.latestNoPrice ?? 0,
2044
- avgPrice: noCost > 0 ? parseFloat(clobPos.positions.no.fillPrice) / 1e6 : 0,
2045
- tokenBalance: parseFloat(clobPos.tokensBalance.no)
2046
- });
2047
- }
2048
- }
2049
- for (const ammPos of response.amm || []) {
2050
- const cost = parseFloat(ammPos.totalBuysCost);
2051
- const value = parseFloat(ammPos.collateralAmount);
2052
- if (cost > 0 || value > 0) {
2053
- positions.push({
2054
- type: "AMM",
2055
- market: ammPos.market,
2056
- side: ammPos.outcomeIndex === 0 ? "YES" : "NO",
2057
- costBasis: cost,
2058
- marketValue: value,
2059
- unrealizedPnl: parseFloat(ammPos.unrealizedPnl),
2060
- realizedPnl: parseFloat(ammPos.realizedPnl),
2061
- currentPrice: ammPos.latestTrade ? parseFloat(ammPos.latestTrade.outcomeTokenPrice) : 0,
2062
- avgPrice: parseFloat(ammPos.averageFillPrice),
2063
- tokenBalance: parseFloat(ammPos.outcomeTokenAmount)
2064
- });
2065
- }
2066
- }
2067
- this.logger.debug("Flattened positions", { count: positions.length });
2068
- return positions;
2069
- }
2070
- /**
2071
- * Calculates portfolio summary statistics from raw API response.
1783
+ * Gets the wallet address.
2072
1784
  *
2073
- * @param response - Portfolio positions response from API
2074
- * @returns Portfolio summary with totals and statistics
1785
+ * @returns Ethereum address of the wallet
2075
1786
  *
2076
1787
  * @example
2077
1788
  * ```typescript
2078
- * const response = await portfolioFetcher.getPositions();
2079
- * const summary = portfolioFetcher.calculateSummary(response);
2080
- *
2081
- * console.log(`Total Portfolio Value: $${(summary.totalValue / 1e6).toFixed(2)}`);
2082
- * console.log(`Total P&L: ${summary.totalUnrealizedPnlPercent.toFixed(2)}%`);
2083
- * console.log(`CLOB Positions: ${summary.breakdown.clob.positions}`);
2084
- * console.log(`AMM Positions: ${summary.breakdown.amm.positions}`);
1789
+ * const address = orderClient.walletAddress;
1790
+ * console.log(`Wallet: ${address}`);
2085
1791
  * ```
2086
1792
  */
2087
- calculateSummary(response) {
2088
- this.logger.debug("Calculating portfolio summary", {
2089
- clobCount: response.clob?.length || 0,
2090
- ammCount: response.amm?.length || 0
2091
- });
2092
- let totalValue = 0;
2093
- let totalCostBasis = 0;
2094
- let totalUnrealizedPnl = 0;
2095
- let totalRealizedPnl = 0;
2096
- let clobPositions = 0;
2097
- let clobValue = 0;
2098
- let clobPnl = 0;
2099
- let ammPositions = 0;
2100
- let ammValue = 0;
2101
- let ammPnl = 0;
2102
- for (const clobPos of response.clob || []) {
2103
- const yesCost = parseFloat(clobPos.positions.yes.cost);
2104
- const yesValue = parseFloat(clobPos.positions.yes.marketValue);
2105
- const yesUnrealizedPnl = parseFloat(clobPos.positions.yes.unrealizedPnl);
2106
- const yesRealizedPnl = parseFloat(clobPos.positions.yes.realisedPnl);
2107
- if (yesCost > 0 || yesValue > 0) {
2108
- clobPositions++;
2109
- totalCostBasis += yesCost;
2110
- totalValue += yesValue;
2111
- totalUnrealizedPnl += yesUnrealizedPnl;
2112
- totalRealizedPnl += yesRealizedPnl;
2113
- clobValue += yesValue;
2114
- clobPnl += yesUnrealizedPnl;
2115
- }
2116
- const noCost = parseFloat(clobPos.positions.no.cost);
2117
- const noValue = parseFloat(clobPos.positions.no.marketValue);
2118
- const noUnrealizedPnl = parseFloat(clobPos.positions.no.unrealizedPnl);
2119
- const noRealizedPnl = parseFloat(clobPos.positions.no.realisedPnl);
2120
- if (noCost > 0 || noValue > 0) {
2121
- clobPositions++;
2122
- totalCostBasis += noCost;
2123
- totalValue += noValue;
2124
- totalUnrealizedPnl += noUnrealizedPnl;
2125
- totalRealizedPnl += noRealizedPnl;
2126
- clobValue += noValue;
2127
- clobPnl += noUnrealizedPnl;
2128
- }
2129
- }
2130
- for (const ammPos of response.amm || []) {
2131
- const cost = parseFloat(ammPos.totalBuysCost);
2132
- const value = parseFloat(ammPos.collateralAmount);
2133
- const unrealizedPnl = parseFloat(ammPos.unrealizedPnl);
2134
- const realizedPnl = parseFloat(ammPos.realizedPnl);
2135
- if (cost > 0 || value > 0) {
2136
- ammPositions++;
2137
- totalCostBasis += cost;
2138
- totalValue += value;
2139
- totalUnrealizedPnl += unrealizedPnl;
2140
- totalRealizedPnl += realizedPnl;
2141
- ammValue += value;
2142
- ammPnl += unrealizedPnl;
2143
- }
2144
- }
2145
- const totalUnrealizedPnlPercent = totalCostBasis > 0 ? totalUnrealizedPnl / totalCostBasis * 100 : 0;
2146
- const uniqueMarkets = /* @__PURE__ */ new Set();
2147
- for (const pos of response.clob || []) {
2148
- uniqueMarkets.add(pos.market.id);
2149
- }
2150
- for (const pos of response.amm || []) {
2151
- uniqueMarkets.add(pos.market.id);
2152
- }
2153
- const summary = {
2154
- totalValue,
2155
- totalCostBasis,
2156
- totalUnrealizedPnl,
2157
- totalRealizedPnl,
2158
- totalUnrealizedPnlPercent,
2159
- positionCount: clobPositions + ammPositions,
2160
- marketCount: uniqueMarkets.size,
2161
- breakdown: {
2162
- clob: {
2163
- positions: clobPositions,
2164
- value: clobValue,
2165
- pnl: clobPnl
2166
- },
2167
- amm: {
2168
- positions: ammPositions,
2169
- value: ammValue,
2170
- pnl: ammPnl
2171
- }
2172
- }
2173
- };
2174
- this.logger.debug("Portfolio summary calculated", summary);
2175
- return summary;
1793
+ get walletAddress() {
1794
+ return this.wallet.address;
2176
1795
  }
2177
1796
  /**
2178
- * Gets positions and calculates summary in a single call.
1797
+ * Gets the owner ID (user ID from profile).
2179
1798
  *
2180
- * @returns Promise resolving to response and summary
2181
- * @throws Error if API request fails or user is not authenticated
1799
+ * @returns Owner ID from user profile, or undefined if not yet loaded
2182
1800
  *
2183
1801
  * @example
2184
1802
  * ```typescript
2185
- * const { response, summary } = await portfolioFetcher.getPortfolio();
2186
- *
2187
- * console.log('Portfolio Summary:');
2188
- * console.log(` Total Value: $${(summary.totalValue / 1e6).toFixed(2)}`);
2189
- * console.log(` Total P&L: $${(summary.totalUnrealizedPnl / 1e6).toFixed(2)}`);
2190
- * console.log(` P&L %: ${summary.totalUnrealizedPnlPercent.toFixed(2)}%`);
2191
- * console.log(`\nCLOB Positions: ${response.clob.length}`);
2192
- * console.log(`AMM Positions: ${response.amm.length}`);
1803
+ * const ownerId = orderClient.ownerId;
1804
+ * if (ownerId) {
1805
+ * console.log(`Owner ID: ${ownerId}`);
1806
+ * }
2193
1807
  * ```
2194
1808
  */
2195
- async getPortfolio() {
2196
- this.logger.debug("Fetching portfolio with summary");
2197
- const response = await this.getPositions();
2198
- const summary = this.calculateSummary(response);
2199
- this.logger.info("Portfolio fetched with summary", {
2200
- positionCount: summary.positionCount,
2201
- totalValueUSDC: summary.totalValue / 1e6,
2202
- pnlPercent: summary.totalUnrealizedPnlPercent
2203
- });
2204
- return { response, summary };
1809
+ get ownerId() {
1810
+ return this.cachedUserData?.userId;
2205
1811
  }
2206
1812
  };
2207
1813
 
@@ -2222,7 +1828,7 @@ var WebSocketClient = class {
2222
1828
  this.pendingListeners = [];
2223
1829
  this.config = {
2224
1830
  url: config.url || DEFAULT_WS_URL,
2225
- sessionCookie: config.sessionCookie || "",
1831
+ apiKey: config.apiKey || process.env.LIMITLESS_API_KEY || "",
2226
1832
  autoReconnect: config.autoReconnect ?? true,
2227
1833
  reconnectDelay: config.reconnectDelay || 1e3,
2228
1834
  maxReconnectAttempts: config.maxReconnectAttempts || Infinity,
@@ -2247,18 +1853,29 @@ var WebSocketClient = class {
2247
1853
  return this.state === "connected" /* CONNECTED */ && this.socket?.connected === true;
2248
1854
  }
2249
1855
  /**
2250
- * Sets the session cookie for authentication.
1856
+ * Sets the API key for authentication.
2251
1857
  *
2252
- * @param sessionCookie - Session cookie value
1858
+ * @param apiKey - API key value
1859
+ *
1860
+ * @remarks
1861
+ * API key is required for authenticated subscriptions (positions, transactions).
1862
+ * If already connected, this will trigger a reconnection with the new API key.
2253
1863
  */
2254
- setSessionCookie(sessionCookie) {
2255
- this.config.sessionCookie = sessionCookie;
1864
+ setApiKey(apiKey) {
1865
+ this.config.apiKey = apiKey;
2256
1866
  if (this.socket?.connected) {
2257
- this.logger.info("Session cookie updated, reconnecting...");
2258
- this.disconnect();
2259
- this.connect();
1867
+ this.logger.info("API key updated, reconnecting...");
1868
+ this.reconnectWithNewAuth();
2260
1869
  }
2261
1870
  }
1871
+ /**
1872
+ * Reconnects with new authentication credentials.
1873
+ * @internal
1874
+ */
1875
+ async reconnectWithNewAuth() {
1876
+ await this.disconnect();
1877
+ await this.connect();
1878
+ }
2262
1879
  /**
2263
1880
  * Connects to the WebSocket server.
2264
1881
  *
@@ -2272,8 +1889,8 @@ var WebSocketClient = class {
2272
1889
  * ```
2273
1890
  */
2274
1891
  async connect() {
2275
- if (this.socket?.connected) {
2276
- this.logger.info("Already connected");
1892
+ if (this.socket?.connected || this.state === "connecting" /* CONNECTING */) {
1893
+ this.logger.info("Already connected or connecting");
2277
1894
  return;
2278
1895
  }
2279
1896
  this.logger.info("Connecting to WebSocket", { url: this.config.url });
@@ -2282,18 +1899,26 @@ var WebSocketClient = class {
2282
1899
  const timeout = setTimeout(() => {
2283
1900
  reject(new Error(`Connection timeout after ${this.config.timeout}ms`));
2284
1901
  }, this.config.timeout);
2285
- const wsUrl = this.config.url.endsWith("/markets") ? this.config.url : `${this.config.url}/markets`;
2286
- this.socket = io(wsUrl, {
1902
+ const wsUrl = this.config.url;
1903
+ const socketOptions = {
2287
1904
  transports: ["websocket"],
2288
1905
  // Use WebSocket transport only
2289
- extraHeaders: {
2290
- cookie: `limitless_session=${this.config.sessionCookie}`
2291
- },
2292
1906
  reconnection: this.config.autoReconnect,
2293
1907
  reconnectionDelay: this.config.reconnectDelay,
2294
- reconnectionAttempts: this.config.maxReconnectAttempts,
1908
+ reconnectionDelayMax: Math.min(this.config.reconnectDelay * 32, 6e4),
1909
+ // Max 60s
1910
+ reconnectionAttempts: this.config.maxReconnectAttempts === Infinity ? 0 : this.config.maxReconnectAttempts,
1911
+ // 0 = infinite
1912
+ randomizationFactor: 0.2,
1913
+ // Add jitter to prevent thundering herd
2295
1914
  timeout: this.config.timeout
2296
- });
1915
+ };
1916
+ if (this.config.apiKey) {
1917
+ socketOptions.extraHeaders = {
1918
+ "X-API-Key": this.config.apiKey
1919
+ };
1920
+ }
1921
+ this.socket = io(wsUrl + "/markets", socketOptions);
2297
1922
  this.attachPendingListeners();
2298
1923
  this.setupEventHandlers();
2299
1924
  this.socket.once("connect", () => {
@@ -2315,12 +1940,14 @@ var WebSocketClient = class {
2315
1940
  /**
2316
1941
  * Disconnects from the WebSocket server.
2317
1942
  *
1943
+ * @returns Promise that resolves when disconnected
1944
+ *
2318
1945
  * @example
2319
1946
  * ```typescript
2320
- * wsClient.disconnect();
1947
+ * await wsClient.disconnect();
2321
1948
  * ```
2322
1949
  */
2323
- disconnect() {
1950
+ async disconnect() {
2324
1951
  if (!this.socket) {
2325
1952
  return;
2326
1953
  }
@@ -2335,13 +1962,13 @@ var WebSocketClient = class {
2335
1962
  *
2336
1963
  * @param channel - Channel to subscribe to
2337
1964
  * @param options - Subscription options
2338
- * @returns Promise that resolves when subscribed
1965
+ * @returns Promise that resolves immediately (kept async for API compatibility)
2339
1966
  * @throws Error if not connected
2340
1967
  *
2341
1968
  * @example
2342
1969
  * ```typescript
2343
1970
  * // Subscribe to orderbook for a specific market
2344
- * await wsClient.subscribe('orderbook', { marketSlug: 'market-123' });
1971
+ * await wsClient.subscribe('orderbook', { marketSlugs: ['market-123'] });
2345
1972
  *
2346
1973
  * // Subscribe to all trades
2347
1974
  * await wsClient.subscribe('trades');
@@ -2354,21 +1981,20 @@ var WebSocketClient = class {
2354
1981
  if (!this.isConnected()) {
2355
1982
  throw new Error("WebSocket not connected. Call connect() first.");
2356
1983
  }
1984
+ const authenticatedChannels = [
1985
+ "subscribe_positions",
1986
+ "subscribe_transactions"
1987
+ ];
1988
+ if (authenticatedChannels.includes(channel) && !this.config.apiKey) {
1989
+ throw new Error(
1990
+ `API key is required for '${channel}' subscription. Please provide an API key in the constructor or set LIMITLESS_API_KEY environment variable. You can generate an API key at https://limitless.exchange`
1991
+ );
1992
+ }
2357
1993
  const subscriptionKey = this.getSubscriptionKey(channel, options);
2358
1994
  this.subscriptions.set(subscriptionKey, options);
2359
1995
  this.logger.info("Subscribing to channel", { channel, options });
2360
- return new Promise((resolve, reject) => {
2361
- this.socket.emit(channel, options, (response) => {
2362
- if (response?.error) {
2363
- this.logger.error("Subscription failed", response.error);
2364
- this.subscriptions.delete(subscriptionKey);
2365
- reject(new Error(response.error));
2366
- } else {
2367
- this.logger.info("Subscribed successfully", { channel, options });
2368
- resolve();
2369
- }
2370
- });
2371
- });
1996
+ this.socket.emit(channel, options);
1997
+ this.logger.info("Subscription request sent", { channel, options });
2372
1998
  }
2373
1999
  /**
2374
2000
  * Unsubscribes from a channel.
@@ -2376,10 +2002,11 @@ var WebSocketClient = class {
2376
2002
  * @param channel - Channel to unsubscribe from
2377
2003
  * @param options - Subscription options (must match subscribe call)
2378
2004
  * @returns Promise that resolves when unsubscribed
2005
+ * @throws Error if not connected or unsubscribe fails
2379
2006
  *
2380
2007
  * @example
2381
2008
  * ```typescript
2382
- * await wsClient.unsubscribe('orderbook', { marketSlug: 'market-123' });
2009
+ * await wsClient.unsubscribe('orderbook', { marketSlugs: ['market-123'] });
2383
2010
  * ```
2384
2011
  */
2385
2012
  async unsubscribe(channel, options = {}) {
@@ -2389,17 +2016,19 @@ var WebSocketClient = class {
2389
2016
  const subscriptionKey = this.getSubscriptionKey(channel, options);
2390
2017
  this.subscriptions.delete(subscriptionKey);
2391
2018
  this.logger.info("Unsubscribing from channel", { channel, options });
2392
- return new Promise((resolve, reject) => {
2393
- this.socket.emit("unsubscribe", { channel, ...options }, (response) => {
2394
- if (response?.error) {
2395
- this.logger.error("Unsubscribe failed", response.error);
2396
- reject(new Error(response.error));
2397
- } else {
2398
- this.logger.info("Unsubscribed successfully", { channel, options });
2399
- resolve();
2400
- }
2401
- });
2402
- });
2019
+ try {
2020
+ const unsubscribeData = { channel, ...options };
2021
+ const response = await this.socket.timeout(5e3).emitWithAck("unsubscribe", unsubscribeData);
2022
+ if (response && typeof response === "object" && "error" in response) {
2023
+ const errorMsg = response.error;
2024
+ this.logger.error("Unsubscribe failed", new Error(errorMsg), { error: errorMsg });
2025
+ throw new Error(`Unsubscribe failed: ${errorMsg}`);
2026
+ }
2027
+ this.logger.info("Unsubscribed successfully", { channel, options });
2028
+ } catch (error) {
2029
+ this.logger.error("Unsubscribe error", error, { channel });
2030
+ throw error;
2031
+ }
2403
2032
  }
2404
2033
  /**
2405
2034
  * Registers an event listener.
@@ -2442,14 +2071,27 @@ var WebSocketClient = class {
2442
2071
  * Removes an event listener.
2443
2072
  *
2444
2073
  * @param event - Event name
2445
- * @param handler - Event handler to remove
2074
+ * @param handler - Event handler to remove (if undefined, removes all handlers for event)
2446
2075
  * @returns This client for chaining
2076
+ *
2077
+ * @example
2078
+ * ```typescript
2079
+ * // Remove specific handler
2080
+ * wsClient.off('orderbookUpdate', myHandler);
2081
+ *
2082
+ * // Remove all handlers for event
2083
+ * wsClient.off('orderbookUpdate');
2084
+ * ```
2447
2085
  */
2448
2086
  off(event, handler) {
2449
2087
  if (!this.socket) {
2450
2088
  return this;
2451
2089
  }
2452
- this.socket.off(event, handler);
2090
+ if (handler === void 0) {
2091
+ this.socket.removeAllListeners(event);
2092
+ } else {
2093
+ this.socket.off(event, handler);
2094
+ }
2453
2095
  return this;
2454
2096
  }
2455
2097
  /**
@@ -2541,8 +2183,7 @@ var WebSocketClient = class {
2541
2183
  };
2542
2184
  export {
2543
2185
  APIError,
2544
- AuthenticatedClient,
2545
- Authenticator,
2186
+ AuthenticationError,
2546
2187
  BASE_SEPOLIA_CHAIN_ID,
2547
2188
  CONTRACT_ADDRESSES,
2548
2189
  ConsoleLogger,
@@ -2550,14 +2191,16 @@ export {
2550
2191
  DEFAULT_CHAIN_ID,
2551
2192
  DEFAULT_WS_URL,
2552
2193
  HttpClient,
2194
+ Market,
2553
2195
  MarketFetcher,
2554
- MessageSigner,
2555
2196
  NoOpLogger,
2556
2197
  OrderBuilder,
2557
2198
  OrderClient,
2558
2199
  OrderSigner,
2559
2200
  OrderType,
2201
+ OrderValidationError,
2560
2202
  PortfolioFetcher,
2203
+ RateLimitError,
2561
2204
  RetryConfig,
2562
2205
  RetryableClient,
2563
2206
  SIGNING_MESSAGE_TEMPLATE,