@limitless-exchange/sdk 0.0.2 → 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
@@ -46,16 +46,11 @@ var Side = /* @__PURE__ */ ((Side2) => {
46
46
  Side2[Side2["SELL"] = 1] = "SELL";
47
47
  return Side2;
48
48
  })(Side || {});
49
- var OrderType = /* @__PURE__ */ ((OrderType3) => {
50
- OrderType3["FOK"] = "FOK";
51
- OrderType3["GTC"] = "GTC";
52
- return OrderType3;
49
+ var OrderType = /* @__PURE__ */ ((OrderType2) => {
50
+ OrderType2["FOK"] = "FOK";
51
+ OrderType2["GTC"] = "GTC";
52
+ return OrderType2;
53
53
  })(OrderType || {});
54
- var MarketType = /* @__PURE__ */ ((MarketType3) => {
55
- MarketType3["CLOB"] = "CLOB";
56
- MarketType3["NEGRISK"] = "NEGRISK";
57
- return MarketType3;
58
- })(MarketType || {});
59
54
  var SignatureType = /* @__PURE__ */ ((SignatureType2) => {
60
55
  SignatureType2[SignatureType2["EOA"] = 0] = "EOA";
61
56
  SignatureType2[SignatureType2["POLY_PROXY"] = 1] = "POLY_PROXY";
@@ -73,250 +68,92 @@ var WebSocketState = /* @__PURE__ */ ((WebSocketState2) => {
73
68
  return WebSocketState2;
74
69
  })(WebSocketState || {});
75
70
 
76
- // src/auth/signer.ts
77
- import { ethers } from "ethers";
78
- var MessageSigner = class {
79
- /**
80
- * Creates a new message signer instance.
81
- *
82
- * @param wallet - Ethers wallet instance for signing
83
- */
84
- constructor(wallet) {
85
- this.wallet = wallet;
86
- }
87
- /**
88
- * Creates authentication headers for API requests.
89
- *
90
- * @param signingMessage - Message to sign from the API
91
- * @returns Promise resolving to signature headers
92
- *
93
- * @example
94
- * ```typescript
95
- * const signer = new MessageSigner(wallet);
96
- * const headers = await signer.createAuthHeaders(message);
97
- * ```
98
- */
99
- async createAuthHeaders(signingMessage) {
100
- const hexMessage = this.stringToHex(signingMessage);
101
- const signature = await this.wallet.signMessage(signingMessage);
102
- const address = this.wallet.address;
103
- const recoveredAddress = ethers.verifyMessage(signingMessage, signature);
104
- if (address.toLowerCase() !== recoveredAddress.toLowerCase()) {
105
- throw new Error("Signature verification failed: address mismatch");
106
- }
107
- return {
108
- "x-account": address,
109
- "x-signing-message": hexMessage,
110
- "x-signature": signature
111
- };
112
- }
113
- /**
114
- * Signs EIP-712 typed data.
115
- *
116
- * @param domain - EIP-712 domain
117
- * @param types - EIP-712 types
118
- * @param value - Value to sign
119
- * @returns Promise resolving to signature string
120
- *
121
- * @example
122
- * ```typescript
123
- * const signature = await signer.signTypedData(domain, types, order);
124
- * ```
125
- */
126
- async signTypedData(domain, types, value) {
127
- return await this.wallet.signTypedData(domain, types, value);
128
- }
129
- /**
130
- * Gets the wallet address.
131
- *
132
- * @returns Ethereum address
133
- */
134
- getAddress() {
135
- return this.wallet.address;
136
- }
137
- /**
138
- * Converts a string to hex format.
139
- *
140
- * @param text - String to convert
141
- * @returns Hex string with 0x prefix
142
- * @internal
143
- */
144
- stringToHex(text) {
145
- return ethers.hexlify(ethers.toUtf8Bytes(text));
146
- }
147
- };
148
-
149
- // src/auth/authenticator.ts
150
- var Authenticator = class {
71
+ // src/types/market-class.ts
72
+ var Market = class _Market {
151
73
  /**
152
- * Creates a new authenticator instance.
153
- *
154
- * @param httpClient - HTTP client for API requests
155
- * @param signer - Message signer for wallet operations
156
- * @param logger - Optional logger for debugging and monitoring (default: no logging)
74
+ * Creates a Market instance.
157
75
  *
158
- * @example
159
- * ```typescript
160
- * // Without logging (default)
161
- * const authenticator = new Authenticator(httpClient, signer);
162
- *
163
- * // With logging
164
- * import { ConsoleLogger } from '@limitless-exchange/sdk';
165
- * const logger = new ConsoleLogger('debug');
166
- * const authenticator = new Authenticator(httpClient, signer, logger);
167
- * ```
76
+ * @param data - Market data from API
77
+ * @param httpClient - HTTP client for making requests
168
78
  */
169
- constructor(httpClient, signer, logger) {
79
+ constructor(data, httpClient) {
80
+ Object.assign(this, data);
170
81
  this.httpClient = httpClient;
171
- this.signer = signer;
172
- 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
+ }
173
85
  }
174
86
  /**
175
- * Gets a signing message from the API.
176
- *
177
- * @returns Promise resolving to signing message string
178
- * @throws Error if API request fails
87
+ * Get user's orders for this market.
179
88
  *
180
- * @example
181
- * ```typescript
182
- * const message = await authenticator.getSigningMessage();
183
- * console.log(message);
184
- * ```
185
- */
186
- async getSigningMessage() {
187
- this.logger.debug("Requesting signing message from API");
188
- const message = await this.httpClient.get("/auth/signing-message");
189
- this.logger.debug("Received signing message", { length: message.length });
190
- return message;
191
- }
192
- /**
193
- * 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.
194
92
  *
195
- * @param options - Login options including client type and smart wallet
196
- * @returns Promise resolving to authentication result
197
- * @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
198
95
  *
199
96
  * @example
200
97
  * ```typescript
201
- * // EOA authentication
202
- * const result = await authenticator.authenticate({ client: 'eoa' });
203
- *
204
- * // ETHERSPOT with smart wallet
205
- * const result = await authenticator.authenticate({
206
- * client: 'etherspot',
207
- * smartWallet: '0x...'
208
- * });
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}`);
209
102
  * ```
210
103
  */
211
- async authenticate(options = {}) {
212
- const client = options.client || "eoa";
213
- this.logger.info("Starting authentication", {
214
- client,
215
- hasSmartWallet: !!options.smartWallet
216
- });
217
- if (client === "etherspot" && !options.smartWallet) {
218
- this.logger.error("Smart wallet address required for ETHERSPOT client");
219
- throw new Error("Smart wallet address is required for ETHERSPOT client");
220
- }
221
- try {
222
- const signingMessage = await this.getSigningMessage();
223
- this.logger.debug("Creating signature headers");
224
- const headers = await this.signer.createAuthHeaders(signingMessage);
225
- this.logger.debug("Sending authentication request", { client });
226
- const response = await this.httpClient.postWithResponse(
227
- "/auth/login",
228
- { client, smartWallet: options.smartWallet },
229
- {
230
- headers,
231
- validateStatus: (status) => status < 500
232
- }
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."
233
108
  );
234
- this.logger.debug("Extracting session cookie from response");
235
- const cookies = this.httpClient.extractCookies(response);
236
- const sessionCookie = cookies["limitless_session"];
237
- if (!sessionCookie) {
238
- this.logger.error("Session cookie not found in response headers");
239
- throw new Error("Failed to obtain session cookie from response");
240
- }
241
- this.httpClient.setSessionCookie(sessionCookie);
242
- this.logger.info("Authentication successful", {
243
- account: response.data.account,
244
- client: response.data.client
245
- });
246
- return {
247
- sessionCookie,
248
- profile: response.data
249
- };
250
- } catch (error) {
251
- this.logger.error("Authentication failed", error, {
252
- client
253
- });
254
- throw error;
255
- }
256
- }
257
- /**
258
- * Verifies the current authentication status.
259
- *
260
- * @param sessionCookie - Session cookie to verify
261
- * @returns Promise resolving to user's Ethereum address
262
- * @throws Error if session is invalid
263
- *
264
- * @example
265
- * ```typescript
266
- * const address = await authenticator.verifyAuth(sessionCookie);
267
- * console.log(`Authenticated as: ${address}`);
268
- * ```
269
- */
270
- async verifyAuth(sessionCookie) {
271
- this.logger.debug("Verifying authentication session");
272
- const originalCookie = this.httpClient["sessionCookie"];
273
- this.httpClient.setSessionCookie(sessionCookie);
274
- try {
275
- const address = await this.httpClient.get("/auth/verify-auth");
276
- this.logger.info("Session verified", { address });
277
- return address;
278
- } catch (error) {
279
- this.logger.error("Session verification failed", error);
280
- throw error;
281
- } finally {
282
- if (originalCookie) {
283
- this.httpClient.setSessionCookie(originalCookie);
284
- } else {
285
- this.httpClient.clearSessionCookie();
286
- }
287
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;
288
113
  }
289
- /**
290
- * Logs out and clears the session.
291
- *
292
- * @param sessionCookie - Session cookie to invalidate
293
- * @throws Error if logout request fails
294
- *
295
- * @example
296
- * ```typescript
297
- * await authenticator.logout(sessionCookie);
298
- * console.log('Logged out successfully');
299
- * ```
300
- */
301
- async logout(sessionCookie) {
302
- this.logger.debug("Logging out session");
303
- const originalCookie = this.httpClient["sessionCookie"];
304
- this.httpClient.setSessionCookie(sessionCookie);
305
- try {
306
- await this.httpClient.post("/auth/logout", {});
307
- this.logger.info("Logout successful");
308
- } catch (error) {
309
- this.logger.error("Logout failed", error);
310
- throw error;
311
- } finally {
312
- if (originalCookie) {
313
- this.httpClient.setSessionCookie(originalCookie);
314
- } else {
315
- this.httpClient.clearSessionCookie();
316
- }
317
- }
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..."
318
140
  }
319
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
+ }
320
157
 
321
158
  // src/api/errors.ts
322
159
  var APIError = class _APIError extends Error {
@@ -362,111 +199,33 @@ var APIError = class _APIError extends Error {
362
199
  return this.status === 401 || this.status === 403;
363
200
  }
364
201
  };
365
-
366
- // src/auth/authenticated-client.ts
367
- var AuthenticatedClient = class {
368
- /**
369
- * Creates a new authenticated client with auto-retry capability.
370
- *
371
- * @param config - Configuration for authenticated client
372
- */
373
- constructor(config) {
374
- this.httpClient = config.httpClient;
375
- this.authenticator = config.authenticator;
376
- this.client = config.client;
377
- this.smartWallet = config.smartWallet;
378
- this.logger = config.logger || new NoOpLogger();
379
- this.maxRetries = config.maxRetries ?? 1;
380
- }
381
- /**
382
- * Executes a function with automatic retry on authentication errors.
383
- *
384
- * @param fn - Function to execute with auth retry
385
- * @returns Promise resolving to the function result
386
- * @throws Error if max retries exceeded or non-auth error occurs
387
- *
388
- * @example
389
- * ```typescript
390
- * // Automatic retry on 401/403
391
- * const positions = await authClient.withRetry(() =>
392
- * portfolioFetcher.getPositions()
393
- * );
394
- *
395
- * // Works with any async operation
396
- * const order = await authClient.withRetry(() =>
397
- * orderClient.createOrder({ ... })
398
- * );
399
- * ```
400
- */
401
- async withRetry(fn) {
402
- let attempts = 0;
403
- while (attempts <= this.maxRetries) {
404
- try {
405
- return await fn();
406
- } catch (error) {
407
- if (error instanceof APIError && error.isAuthError() && attempts < this.maxRetries) {
408
- this.logger.info("Authentication expired, re-authenticating...", {
409
- attempt: attempts + 1,
410
- maxRetries: this.maxRetries
411
- });
412
- await this.reauthenticate();
413
- attempts++;
414
- continue;
415
- }
416
- throw error;
417
- }
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);
418
208
  }
419
- throw new Error("Unexpected error: exceeded max retries");
420
- }
421
- /**
422
- * Re-authenticates with the API.
423
- *
424
- * @internal
425
- */
426
- async reauthenticate() {
427
- this.logger.debug("Re-authenticating with API");
428
- await this.authenticator.authenticate({
429
- client: this.client,
430
- smartWallet: this.smartWallet
431
- });
432
- this.logger.info("Re-authentication successful");
433
209
  }
434
210
  };
435
-
436
- // src/api/http.ts
437
- import axios from "axios";
438
- import http from "http";
439
- import https from "https";
440
-
441
- // src/utils/constants.ts
442
- var DEFAULT_API_URL = "https://api.limitless.exchange";
443
- var DEFAULT_WS_URL = "wss://ws.limitless.exchange";
444
- var DEFAULT_CHAIN_ID = 8453;
445
- var BASE_SEPOLIA_CHAIN_ID = 84532;
446
- var SIGNING_MESSAGE_TEMPLATE = "Welcome to Limitless.exchange! Please sign this message to verify your identity.\n\nNonce: {NONCE}";
447
- var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
448
- var CONTRACT_ADDRESSES = {
449
- // Base mainnet (chainId: 8453)
450
- 8453: {
451
- CLOB: "0xa4409D988CA2218d956BeEFD3874100F444f0DC3",
452
- NEGRISK: "0x5a38afc17F7E97ad8d6C547ddb837E40B4aEDfC6"
453
- },
454
- // Base Sepolia testnet (chainId: 84532)
455
- 84532: {
456
- CLOB: "0x...",
457
- // Add testnet addresses when available
458
- NEGRISK: "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
+ }
459
218
  }
460
219
  };
461
- function getContractAddress(marketType, chainId = DEFAULT_CHAIN_ID) {
462
- const addresses = CONTRACT_ADDRESSES[chainId];
463
- if (!addresses) {
464
- throw new Error(
465
- `No contract addresses configured for chainId ${chainId}. Supported chains: ${Object.keys(CONTRACT_ADDRESSES).join(", ")}`
466
- );
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
+ }
467
227
  }
468
- return addresses[marketType];
469
- }
228
+ };
470
229
 
471
230
  // src/api/http.ts
472
231
  var HttpClient = class {
@@ -476,8 +235,13 @@ var HttpClient = class {
476
235
  * @param config - Configuration options for the HTTP client
477
236
  */
478
237
  constructor(config = {}) {
479
- this.sessionCookie = config.sessionCookie;
238
+ this.apiKey = config.apiKey || process.env.LIMITLESS_API_KEY;
480
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
+ }
481
245
  const keepAlive = config.keepAlive !== false;
482
246
  const maxSockets = config.maxSockets || 50;
483
247
  const maxFreeSockets = config.maxFreeSockets || 10;
@@ -520,13 +284,17 @@ var HttpClient = class {
520
284
  setupInterceptors() {
521
285
  this.client.interceptors.request.use(
522
286
  (config) => {
523
- if (this.sessionCookie) {
524
- config.headers["Cookie"] = `limitless_session=${this.sessionCookie}`;
287
+ if (this.apiKey) {
288
+ config.headers["X-API-Key"] = this.apiKey;
525
289
  }
526
290
  const fullUrl = `${config.baseURL || ""}${config.url || ""}`;
527
291
  const method = config.method?.toUpperCase() || "GET";
292
+ const logHeaders = { ...config.headers };
293
+ if (logHeaders["X-API-Key"]) {
294
+ logHeaders["X-API-Key"] = "***";
295
+ }
528
296
  this.logger.debug(`\u2192 ${method} ${fullUrl}`, {
529
- headers: config.headers,
297
+ headers: logHeaders,
530
298
  body: config.data
531
299
  });
532
300
  return config;
@@ -567,7 +335,15 @@ var HttpClient = class {
567
335
  message = String(data);
568
336
  }
569
337
  }
570
- 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
+ }
571
347
  } else if (error.request) {
572
348
  throw new Error("No response received from API");
573
349
  } else {
@@ -577,18 +353,18 @@ var HttpClient = class {
577
353
  );
578
354
  }
579
355
  /**
580
- * Sets the session cookie for authenticated requests.
356
+ * Sets the API key for authenticated requests.
581
357
  *
582
- * @param cookie - Session cookie value
358
+ * @param apiKey - API key value
583
359
  */
584
- setSessionCookie(cookie) {
585
- this.sessionCookie = cookie;
360
+ setApiKey(apiKey) {
361
+ this.apiKey = apiKey;
586
362
  }
587
363
  /**
588
- * Clears the session cookie.
364
+ * Clears the API key.
589
365
  */
590
- clearSessionCookie() {
591
- this.sessionCookie = void 0;
366
+ clearApiKey() {
367
+ this.apiKey = void 0;
592
368
  }
593
369
  /**
594
370
  * Performs a GET request.
@@ -613,19 +389,6 @@ var HttpClient = class {
613
389
  const response = await this.client.post(url, data, config);
614
390
  return response.data;
615
391
  }
616
- /**
617
- * Performs a POST request and returns the full response object.
618
- * Useful when you need access to response headers (e.g., for cookie extraction).
619
- *
620
- * @param url - Request URL
621
- * @param data - Request body data
622
- * @param config - Additional request configuration
623
- * @returns Promise resolving to the full AxiosResponse object
624
- * @internal
625
- */
626
- async postWithResponse(url, data, config) {
627
- return await this.client.post(url, data, config);
628
- }
629
392
  /**
630
393
  * Performs a DELETE request.
631
394
  *
@@ -648,26 +411,6 @@ var HttpClient = class {
648
411
  const response = await this.client.delete(url, deleteConfig);
649
412
  return response.data;
650
413
  }
651
- /**
652
- * Extracts cookies from response headers.
653
- *
654
- * @param response - Axios response object
655
- * @returns Object containing parsed cookies
656
- * @internal
657
- */
658
- extractCookies(response) {
659
- const setCookie = response.headers["set-cookie"];
660
- if (!setCookie) return {};
661
- const cookies = {};
662
- const cookieStrings = Array.isArray(setCookie) ? setCookie : [setCookie];
663
- for (const cookieString of cookieStrings) {
664
- const parts = cookieString.split(";")[0].split("=");
665
- if (parts.length === 2) {
666
- cookies[parts[0].trim()] = parts[1].trim();
667
- }
668
- }
669
- return cookies;
670
- }
671
414
  };
672
415
 
673
416
  // src/api/retry.ts
@@ -852,7 +595,7 @@ var RetryableClient = class {
852
595
  };
853
596
 
854
597
  // src/orders/builder.ts
855
- import { ethers as ethers2 } from "ethers";
598
+ import { ethers } from "ethers";
856
599
  var ZERO_ADDRESS2 = "0x0000000000000000000000000000000000000000";
857
600
  var DEFAULT_PRICE_TICK = 1e-3;
858
601
  var OrderBuilder = class {
@@ -891,8 +634,7 @@ var OrderBuilder = class {
891
634
  * const fokOrder = builder.buildOrder({
892
635
  * tokenId: '123456',
893
636
  * makerAmount: 50, // 50 USDC to spend
894
- * side: Side.BUY,
895
- * marketType: MarketType.CLOB
637
+ * side: Side.BUY
896
638
  * });
897
639
  *
898
640
  * // GTC order (price + size)
@@ -900,8 +642,7 @@ var OrderBuilder = class {
900
642
  * tokenId: '123456',
901
643
  * price: 0.38,
902
644
  * size: 22.123, // Will be rounded to tick-aligned: 22.123 shares
903
- * side: Side.BUY,
904
- * marketType: MarketType.CLOB
645
+ * side: Side.BUY
905
646
  * });
906
647
  * ```
907
648
  */
@@ -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) {
@@ -1176,8 +917,7 @@ var OrderSigner = class {
1176
917
  * ```typescript
1177
918
  * const signature = await signer.signOrder(unsignedOrder, {
1178
919
  * chainId: 8453,
1179
- * contractAddress: '0x...',
1180
- * marketType: MarketType.CLOB
920
+ * contractAddress: '0x...'
1181
921
  * });
1182
922
  * ```
1183
923
  */
@@ -1185,7 +925,7 @@ var OrderSigner = class {
1185
925
  this.logger.debug("Signing order with EIP-712", {
1186
926
  tokenId: order.tokenId,
1187
927
  side: order.side,
1188
- marketType: config.marketType
928
+ verifyingContract: config.contractAddress
1189
929
  });
1190
930
  const walletAddress = await this.wallet.getAddress();
1191
931
  if (walletAddress.toLowerCase() !== order.signer.toLowerCase()) {
@@ -1211,11 +951,11 @@ var OrderSigner = class {
1211
951
  signatureType: order.signatureType
1212
952
  };
1213
953
  this.logger.debug("EIP-712 Order Value", orderValue);
1214
- console.log("[OrderSigner] Full signing payload:", JSON.stringify({
954
+ this.logger.debug("Full signing payload", {
1215
955
  domain,
1216
956
  types: this.getTypes(),
1217
957
  value: orderValue
1218
- }, null, 2));
958
+ });
1219
959
  try {
1220
960
  const signature = await this.wallet.signTypedData(domain, types, orderValue);
1221
961
  this.logger.info("Successfully generated EIP-712 signature", {
@@ -1275,11 +1015,11 @@ var OrderSigner = class {
1275
1015
  };
1276
1016
 
1277
1017
  // src/orders/validator.ts
1278
- import { ethers as ethers3 } from "ethers";
1279
- var ValidationError = class extends Error {
1018
+ import { ethers as ethers2 } from "ethers";
1019
+ var OrderValidationError = class extends Error {
1280
1020
  constructor(message) {
1281
1021
  super(message);
1282
- this.name = "ValidationError";
1022
+ this.name = "OrderValidationError";
1283
1023
  }
1284
1024
  };
1285
1025
  function isFOKOrder(args) {
@@ -1287,418 +1027,149 @@ function isFOKOrder(args) {
1287
1027
  }
1288
1028
  function validateOrderArgs(args) {
1289
1029
  if (!args.tokenId) {
1290
- throw new ValidationError("TokenId is required");
1030
+ throw new OrderValidationError("TokenId is required");
1291
1031
  }
1292
1032
  if (args.tokenId === "0") {
1293
- throw new ValidationError("TokenId cannot be zero");
1033
+ throw new OrderValidationError("TokenId cannot be zero");
1294
1034
  }
1295
1035
  if (!/^\d+$/.test(args.tokenId)) {
1296
- throw new ValidationError(`Invalid tokenId format: ${args.tokenId}`);
1036
+ throw new OrderValidationError(`Invalid tokenId format: ${args.tokenId}`);
1297
1037
  }
1298
- if (args.taker && !ethers3.isAddress(args.taker)) {
1299
- 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}`);
1300
1040
  }
1301
1041
  if (args.expiration !== void 0) {
1302
1042
  if (!/^\d+$/.test(args.expiration)) {
1303
- throw new ValidationError(`Invalid expiration format: ${args.expiration}`);
1043
+ throw new OrderValidationError(`Invalid expiration format: ${args.expiration}`);
1304
1044
  }
1305
1045
  }
1306
1046
  if (args.nonce !== void 0) {
1307
1047
  if (!Number.isInteger(args.nonce) || args.nonce < 0) {
1308
- throw new ValidationError(`Invalid nonce: ${args.nonce}`);
1048
+ throw new OrderValidationError(`Invalid nonce: ${args.nonce}`);
1309
1049
  }
1310
1050
  }
1311
1051
  if (isFOKOrder(args)) {
1312
1052
  if (typeof args.makerAmount !== "number" || isNaN(args.makerAmount)) {
1313
- throw new ValidationError("Amount must be a valid number");
1053
+ throw new OrderValidationError("Amount must be a valid number");
1314
1054
  }
1315
1055
  if (args.makerAmount <= 0) {
1316
- throw new ValidationError(`Amount must be positive, got: ${args.makerAmount}`);
1056
+ throw new OrderValidationError(`Amount must be positive, got: ${args.makerAmount}`);
1317
1057
  }
1318
1058
  const amountStr = args.makerAmount.toString();
1319
1059
  const decimalIndex = amountStr.indexOf(".");
1320
1060
  if (decimalIndex !== -1) {
1321
1061
  const decimalPlaces = amountStr.length - decimalIndex - 1;
1322
1062
  if (decimalPlaces > 2) {
1323
- throw new ValidationError(
1063
+ throw new OrderValidationError(
1324
1064
  `Amount must have max 2 decimal places, got: ${args.makerAmount} (${decimalPlaces} decimals)`
1325
1065
  );
1326
1066
  }
1327
1067
  }
1328
1068
  } else {
1329
1069
  if (typeof args.price !== "number" || isNaN(args.price)) {
1330
- throw new ValidationError("Price must be a valid number");
1070
+ throw new OrderValidationError("Price must be a valid number");
1331
1071
  }
1332
1072
  if (args.price < 0 || args.price > 1) {
1333
- 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}`);
1334
1074
  }
1335
1075
  if (typeof args.size !== "number" || isNaN(args.size)) {
1336
- throw new ValidationError("Size must be a valid number");
1076
+ throw new OrderValidationError("Size must be a valid number");
1337
1077
  }
1338
1078
  if (args.size <= 0) {
1339
- throw new ValidationError(`Size must be positive, got: ${args.size}`);
1079
+ throw new OrderValidationError(`Size must be positive, got: ${args.size}`);
1340
1080
  }
1341
1081
  }
1342
1082
  }
1343
1083
  function validateUnsignedOrder(order) {
1344
- if (!ethers3.isAddress(order.maker)) {
1345
- throw new ValidationError(`Invalid maker address: ${order.maker}`);
1084
+ if (!ethers2.isAddress(order.maker)) {
1085
+ throw new OrderValidationError(`Invalid maker address: ${order.maker}`);
1346
1086
  }
1347
- if (!ethers3.isAddress(order.signer)) {
1348
- throw new ValidationError(`Invalid signer address: ${order.signer}`);
1087
+ if (!ethers2.isAddress(order.signer)) {
1088
+ throw new OrderValidationError(`Invalid signer address: ${order.signer}`);
1349
1089
  }
1350
- if (!ethers3.isAddress(order.taker)) {
1351
- throw new ValidationError(`Invalid taker address: ${order.taker}`);
1090
+ if (!ethers2.isAddress(order.taker)) {
1091
+ throw new OrderValidationError(`Invalid taker address: ${order.taker}`);
1352
1092
  }
1353
1093
  if (!order.makerAmount || order.makerAmount === 0) {
1354
- throw new ValidationError("MakerAmount must be greater than zero");
1094
+ throw new OrderValidationError("MakerAmount must be greater than zero");
1355
1095
  }
1356
1096
  if (!order.takerAmount || order.takerAmount === 0) {
1357
- throw new ValidationError("TakerAmount must be greater than zero");
1097
+ throw new OrderValidationError("TakerAmount must be greater than zero");
1358
1098
  }
1359
1099
  if (typeof order.makerAmount !== "number" || order.makerAmount <= 0) {
1360
- throw new ValidationError(`Invalid makerAmount: ${order.makerAmount}`);
1100
+ throw new OrderValidationError(`Invalid makerAmount: ${order.makerAmount}`);
1361
1101
  }
1362
1102
  if (typeof order.takerAmount !== "number" || order.takerAmount <= 0) {
1363
- throw new ValidationError(`Invalid takerAmount: ${order.takerAmount}`);
1103
+ throw new OrderValidationError(`Invalid takerAmount: ${order.takerAmount}`);
1364
1104
  }
1365
1105
  if (!/^\d+$/.test(order.tokenId)) {
1366
- throw new ValidationError(`Invalid tokenId format: ${order.tokenId}`);
1106
+ throw new OrderValidationError(`Invalid tokenId format: ${order.tokenId}`);
1367
1107
  }
1368
1108
  if (!/^\d+$/.test(order.expiration)) {
1369
- throw new ValidationError(`Invalid expiration format: ${order.expiration}`);
1109
+ throw new OrderValidationError(`Invalid expiration format: ${order.expiration}`);
1370
1110
  }
1371
1111
  if (!Number.isInteger(order.salt) || order.salt <= 0) {
1372
- throw new ValidationError(`Invalid salt: ${order.salt}`);
1112
+ throw new OrderValidationError(`Invalid salt: ${order.salt}`);
1373
1113
  }
1374
1114
  if (!Number.isInteger(order.nonce) || order.nonce < 0) {
1375
- throw new ValidationError(`Invalid nonce: ${order.nonce}`);
1115
+ throw new OrderValidationError(`Invalid nonce: ${order.nonce}`);
1376
1116
  }
1377
1117
  if (!Number.isInteger(order.feeRateBps) || order.feeRateBps < 0) {
1378
- throw new ValidationError(`Invalid feeRateBps: ${order.feeRateBps}`);
1118
+ throw new OrderValidationError(`Invalid feeRateBps: ${order.feeRateBps}`);
1379
1119
  }
1380
1120
  if (order.side !== 0 && order.side !== 1) {
1381
- 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)`);
1382
1122
  }
1383
1123
  if (!Number.isInteger(order.signatureType) || order.signatureType < 0) {
1384
- throw new ValidationError(`Invalid signatureType: ${order.signatureType}`);
1124
+ throw new OrderValidationError(`Invalid signatureType: ${order.signatureType}`);
1385
1125
  }
1386
1126
  if (order.price !== void 0) {
1387
1127
  if (typeof order.price !== "number" || isNaN(order.price)) {
1388
- throw new ValidationError("Price must be a valid number");
1128
+ throw new OrderValidationError("Price must be a valid number");
1389
1129
  }
1390
1130
  if (order.price < 0 || order.price > 1) {
1391
- 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}`);
1392
1132
  }
1393
1133
  }
1394
1134
  }
1395
1135
  function validateSignedOrder(order) {
1396
1136
  validateUnsignedOrder(order);
1397
1137
  if (!order.signature) {
1398
- throw new ValidationError("Signature is required");
1138
+ throw new OrderValidationError("Signature is required");
1399
1139
  }
1400
1140
  if (!order.signature.startsWith("0x")) {
1401
- throw new ValidationError("Signature must start with 0x");
1141
+ throw new OrderValidationError("Signature must start with 0x");
1402
1142
  }
1403
1143
  if (order.signature.length !== 132) {
1404
- throw new ValidationError(
1144
+ throw new OrderValidationError(
1405
1145
  `Invalid signature length: ${order.signature.length}. Expected 132 characters.`
1406
1146
  );
1407
1147
  }
1408
1148
  if (!/^0x[0-9a-fA-F]{130}$/.test(order.signature)) {
1409
- throw new ValidationError("Signature must be valid hex string");
1149
+ throw new OrderValidationError("Signature must be valid hex string");
1410
1150
  }
1411
1151
  }
1412
1152
 
1413
- // src/orders/client.ts
1414
- var OrderClient = class {
1153
+ // src/markets/fetcher.ts
1154
+ var MarketFetcher = class {
1415
1155
  /**
1416
- * Creates a new order client instance.
1156
+ * Creates a new market fetcher instance.
1417
1157
  *
1418
- * @param config - Order client configuration
1158
+ * @param httpClient - HTTP client for API requests
1159
+ * @param logger - Optional logger for debugging (default: no logging)
1419
1160
  *
1420
- * @throws Error if neither marketType nor signingConfig is provided
1161
+ * @example
1162
+ * ```typescript
1163
+ * const fetcher = new MarketFetcher(httpClient);
1164
+ * ```
1421
1165
  */
1422
- constructor(config) {
1423
- this.httpClient = config.httpClient;
1424
- this.logger = config.logger || new NoOpLogger();
1425
- this.ownerId = config.userData.userId;
1426
- const feeRateBps = config.userData.feeRateBps;
1427
- this.orderBuilder = new OrderBuilder(config.wallet.address, feeRateBps, 1e-3);
1428
- this.orderSigner = new OrderSigner(config.wallet, this.logger);
1429
- if (config.signingConfig) {
1430
- this.signingConfig = config.signingConfig;
1431
- } else if (config.marketType) {
1432
- const chainId = parseInt(process.env.CHAIN_ID || "8453");
1433
- const contractAddress = config.marketType === "NEGRISK" /* NEGRISK */ ? process.env.NEGRISK_CONTRACT_ADDRESS || getContractAddress("NEGRISK", chainId) : process.env.CLOB_CONTRACT_ADDRESS || getContractAddress("CLOB", chainId);
1434
- this.signingConfig = {
1435
- chainId,
1436
- contractAddress,
1437
- marketType: config.marketType
1438
- };
1439
- this.logger.info("Auto-configured signing", {
1440
- chainId,
1441
- contractAddress,
1442
- marketType: config.marketType
1443
- });
1444
- } else {
1445
- const chainId = parseInt(process.env.CHAIN_ID || "8453");
1446
- const contractAddress = process.env.CLOB_CONTRACT_ADDRESS || getContractAddress("CLOB", chainId);
1447
- this.signingConfig = {
1448
- chainId,
1449
- contractAddress,
1450
- marketType: "CLOB" /* CLOB */
1451
- };
1452
- this.logger.debug("Using default CLOB configuration", {
1453
- chainId,
1454
- contractAddress
1455
- });
1456
- }
1166
+ constructor(httpClient, logger) {
1167
+ this.httpClient = httpClient;
1168
+ this.logger = logger || new NoOpLogger();
1169
+ this.venueCache = /* @__PURE__ */ new Map();
1457
1170
  }
1458
1171
  /**
1459
- * Creates and submits a new order.
1460
- *
1461
- * @remarks
1462
- * This method handles the complete order creation flow:
1463
- * 1. Build unsigned order
1464
- * 2. Sign with EIP-712
1465
- * 3. Submit to API
1466
- *
1467
- * @param params - Order parameters
1468
- * @returns Promise resolving to order response
1469
- *
1470
- * @throws Error if order creation fails
1471
- *
1472
- * @example
1473
- * ```typescript
1474
- * const order = await orderClient.createOrder({
1475
- * tokenId: '123456',
1476
- * price: 0.65,
1477
- * size: 100,
1478
- * side: Side.BUY,
1479
- * orderType: OrderType.GTC,
1480
- * marketSlug: 'market-slug'
1481
- * });
1482
- *
1483
- * console.log(`Order created: ${order.order.id}`);
1484
- * ```
1485
- */
1486
- async createOrder(params) {
1487
- this.logger.info("Creating order", {
1488
- side: params.side,
1489
- orderType: params.orderType,
1490
- marketSlug: params.marketSlug
1491
- });
1492
- const unsignedOrder = this.orderBuilder.buildOrder(params);
1493
- this.logger.debug("Built unsigned order", {
1494
- salt: unsignedOrder.salt,
1495
- makerAmount: unsignedOrder.makerAmount,
1496
- takerAmount: unsignedOrder.takerAmount
1497
- });
1498
- const signature = await this.orderSigner.signOrder(
1499
- unsignedOrder,
1500
- this.signingConfig
1501
- );
1502
- const payload = {
1503
- order: {
1504
- ...unsignedOrder,
1505
- signature
1506
- },
1507
- orderType: params.orderType,
1508
- marketSlug: params.marketSlug,
1509
- ownerId: this.ownerId
1510
- };
1511
- this.logger.debug("Submitting order to API");
1512
- console.log("[OrderClient] Full API request payload:", JSON.stringify(payload, null, 2));
1513
- const apiResponse = await this.httpClient.post(
1514
- "/orders",
1515
- payload
1516
- );
1517
- this.logger.info("Order created successfully", {
1518
- orderId: apiResponse.order.id
1519
- });
1520
- return this.transformOrderResponse(apiResponse);
1521
- }
1522
- /**
1523
- * Transforms raw API response to clean OrderResponse DTO.
1524
- *
1525
- * @param apiResponse - Raw API response with nested objects
1526
- * @returns Clean OrderResponse with only essential fields
1527
- *
1528
- * @internal
1529
- */
1530
- transformOrderResponse(apiResponse) {
1531
- const cleanOrder = {
1532
- order: {
1533
- id: apiResponse.order.id,
1534
- createdAt: apiResponse.order.createdAt,
1535
- makerAmount: apiResponse.order.makerAmount,
1536
- takerAmount: apiResponse.order.takerAmount,
1537
- expiration: apiResponse.order.expiration,
1538
- signatureType: apiResponse.order.signatureType,
1539
- salt: apiResponse.order.salt,
1540
- maker: apiResponse.order.maker,
1541
- signer: apiResponse.order.signer,
1542
- taker: apiResponse.order.taker,
1543
- tokenId: apiResponse.order.tokenId,
1544
- side: apiResponse.order.side,
1545
- feeRateBps: apiResponse.order.feeRateBps,
1546
- nonce: apiResponse.order.nonce,
1547
- signature: apiResponse.order.signature,
1548
- orderType: apiResponse.order.orderType,
1549
- price: apiResponse.order.price,
1550
- marketId: apiResponse.order.marketId
1551
- }
1552
- };
1553
- if (apiResponse.makerMatches && apiResponse.makerMatches.length > 0) {
1554
- cleanOrder.makerMatches = apiResponse.makerMatches.map((match) => ({
1555
- id: match.id,
1556
- createdAt: match.createdAt,
1557
- matchedSize: match.matchedSize,
1558
- orderId: match.orderId
1559
- }));
1560
- }
1561
- return cleanOrder;
1562
- }
1563
- /**
1564
- * Cancels an existing order by ID.
1565
- *
1566
- * @param orderId - Order ID to cancel
1567
- * @returns Promise resolving to cancellation message
1568
- *
1569
- * @throws Error if cancellation fails
1570
- *
1571
- * @example
1572
- * ```typescript
1573
- * const result = await orderClient.cancel('order-id-123');
1574
- * console.log(result.message); // "Order canceled successfully"
1575
- * ```
1576
- */
1577
- async cancel(orderId) {
1578
- this.logger.info("Cancelling order", { orderId });
1579
- const response = await this.httpClient.delete(
1580
- `/orders/${orderId}`
1581
- );
1582
- this.logger.info("Order cancellation response", {
1583
- orderId,
1584
- message: response.message
1585
- });
1586
- return response;
1587
- }
1588
- /**
1589
- * Cancels all orders for a specific market.
1590
- *
1591
- * @param marketSlug - Market slug to cancel all orders for
1592
- * @returns Promise resolving to cancellation message
1593
- *
1594
- * @throws Error if cancellation fails
1595
- *
1596
- * @example
1597
- * ```typescript
1598
- * const result = await orderClient.cancelAll('market-slug-123');
1599
- * console.log(result.message); // "Orders canceled successfully"
1600
- * ```
1601
- */
1602
- async cancelAll(marketSlug) {
1603
- this.logger.info("Cancelling all orders for market", { marketSlug });
1604
- const response = await this.httpClient.delete(
1605
- `/orders/all/${marketSlug}`
1606
- );
1607
- this.logger.info("All orders cancellation response", {
1608
- marketSlug,
1609
- message: response.message
1610
- });
1611
- return response;
1612
- }
1613
- /**
1614
- * @deprecated Use `cancel()` instead
1615
- */
1616
- async cancelOrder(orderId) {
1617
- await this.cancel(orderId);
1618
- }
1619
- /**
1620
- * Gets an order by ID.
1621
- *
1622
- * @param orderId - Order ID to fetch
1623
- * @returns Promise resolving to order details
1624
- *
1625
- * @throws Error if order not found
1626
- *
1627
- * @example
1628
- * ```typescript
1629
- * const order = await orderClient.getOrder('order-id-123');
1630
- * console.log(order.order.side);
1631
- * ```
1632
- */
1633
- async getOrder(orderId) {
1634
- this.logger.debug("Fetching order", { orderId });
1635
- const response = await this.httpClient.get(
1636
- `/orders/${orderId}`
1637
- );
1638
- return response;
1639
- }
1640
- /**
1641
- * Builds an unsigned order without submitting.
1642
- *
1643
- * @remarks
1644
- * Useful for advanced use cases where you need the unsigned order
1645
- * before signing and submission.
1646
- *
1647
- * @param params - Order parameters
1648
- * @returns Unsigned order
1649
- *
1650
- * @example
1651
- * ```typescript
1652
- * const unsignedOrder = orderClient.buildUnsignedOrder({
1653
- * tokenId: '123456',
1654
- * price: 0.65,
1655
- * size: 100,
1656
- * side: Side.BUY
1657
- * });
1658
- * ```
1659
- */
1660
- buildUnsignedOrder(params) {
1661
- return this.orderBuilder.buildOrder(params);
1662
- }
1663
- /**
1664
- * Signs an unsigned order without submitting.
1665
- *
1666
- * @remarks
1667
- * Useful for advanced use cases where you need to inspect
1668
- * the signature before submission.
1669
- *
1670
- * @param order - Unsigned order to sign
1671
- * @returns Promise resolving to signature
1672
- *
1673
- * @example
1674
- * ```typescript
1675
- * const signature = await orderClient.signOrder(unsignedOrder);
1676
- * ```
1677
- */
1678
- async signOrder(order) {
1679
- return await this.orderSigner.signOrder(order, this.signingConfig);
1680
- }
1681
- };
1682
-
1683
- // src/markets/fetcher.ts
1684
- var MarketFetcher = class {
1685
- /**
1686
- * Creates a new market fetcher instance.
1687
- *
1688
- * @param httpClient - HTTP client for API requests
1689
- * @param logger - Optional logger for debugging (default: no logging)
1690
- *
1691
- * @example
1692
- * ```typescript
1693
- * const fetcher = new MarketFetcher(httpClient);
1694
- * ```
1695
- */
1696
- constructor(httpClient, logger) {
1697
- this.httpClient = httpClient;
1698
- this.logger = logger || new NoOpLogger();
1699
- }
1700
- /**
1701
- * Gets active markets with query parameters and pagination support.
1172
+ * Gets active markets with query parameters and pagination support.
1702
1173
  *
1703
1174
  * @param params - Query parameters for filtering and pagination
1704
1175
  * @returns Promise resolving to active markets response
@@ -1737,13 +1208,18 @@ var MarketFetcher = class {
1737
1208
  this.logger.debug("Fetching active markets", { params });
1738
1209
  try {
1739
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
+ };
1740
1216
  this.logger.info("Active markets fetched successfully", {
1741
- count: response.data.length,
1217
+ count: markets.length,
1742
1218
  total: response.totalMarketsCount,
1743
1219
  sortBy: params?.sortBy,
1744
1220
  page: params?.page
1745
1221
  });
1746
- return response;
1222
+ return result;
1747
1223
  } catch (error) {
1748
1224
  this.logger.error("Failed to fetch active markets", error, { params });
1749
1225
  throw error;
@@ -1752,20 +1228,48 @@ var MarketFetcher = class {
1752
1228
  /**
1753
1229
  * Gets a single market by slug.
1754
1230
  *
1231
+ * @remarks
1232
+ * Automatically caches venue information for efficient order signing.
1233
+ * Always call this method before creating orders to ensure venue data
1234
+ * is available and avoid additional API requests.
1235
+ *
1755
1236
  * @param slug - Market slug identifier
1756
1237
  * @returns Promise resolving to market details
1757
1238
  * @throws Error if API request fails or market not found
1758
1239
  *
1759
1240
  * @example
1760
1241
  * ```typescript
1242
+ * // Get market
1761
1243
  * const market = await fetcher.getMarket('bitcoin-price-2024');
1762
1244
  * console.log(`Market: ${market.title}`);
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
+ *
1250
+ * // Venue is now cached for order signing
1251
+ * await orderClient.createOrder({
1252
+ * marketSlug: 'bitcoin-price-2024',
1253
+ * ...
1254
+ * });
1763
1255
  * ```
1764
1256
  */
1765
1257
  async getMarket(slug) {
1766
1258
  this.logger.debug("Fetching market", { slug });
1767
1259
  try {
1768
- 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);
1262
+ if (market.venue) {
1263
+ this.venueCache.set(slug, market.venue);
1264
+ this.logger.debug("Venue cached for order signing", {
1265
+ slug,
1266
+ exchange: market.venue.exchange,
1267
+ adapter: market.venue.adapter,
1268
+ cacheSize: this.venueCache.size
1269
+ });
1270
+ } else {
1271
+ this.logger.warn("Market has no venue data", { slug });
1272
+ }
1769
1273
  this.logger.info("Market fetched successfully", {
1770
1274
  slug,
1771
1275
  title: market.title
@@ -1776,6 +1280,36 @@ var MarketFetcher = class {
1776
1280
  throw error;
1777
1281
  }
1778
1282
  }
1283
+ /**
1284
+ * Gets cached venue information for a market.
1285
+ *
1286
+ * @remarks
1287
+ * Returns venue data previously cached by getMarket() call.
1288
+ * Used internally by OrderClient for efficient order signing.
1289
+ *
1290
+ * @param slug - Market slug identifier
1291
+ * @returns Cached venue information, or undefined if not in cache
1292
+ *
1293
+ * @example
1294
+ * ```typescript
1295
+ * const venue = fetcher.getVenue('bitcoin-price-2024');
1296
+ * if (venue) {
1297
+ * console.log(`Exchange: ${venue.exchange}`);
1298
+ * }
1299
+ * ```
1300
+ */
1301
+ getVenue(slug) {
1302
+ const venue = this.venueCache.get(slug);
1303
+ if (venue) {
1304
+ this.logger.debug("Venue cache hit", {
1305
+ slug,
1306
+ exchange: venue.exchange
1307
+ });
1308
+ } else {
1309
+ this.logger.debug("Venue cache miss", { slug });
1310
+ }
1311
+ return venue;
1312
+ }
1779
1313
  /**
1780
1314
  * Gets the orderbook for a CLOB market.
1781
1315
  *
@@ -1807,335 +1341,473 @@ var MarketFetcher = class {
1807
1341
  throw error;
1808
1342
  }
1809
1343
  }
1344
+ };
1345
+
1346
+ // src/portfolio/fetcher.ts
1347
+ var PortfolioFetcher = class {
1348
+ /**
1349
+ * Creates a new portfolio fetcher instance.
1350
+ *
1351
+ * @param httpClient - Authenticated HTTP client for API requests
1352
+ * @param logger - Optional logger for debugging (default: no logging)
1353
+ *
1354
+ * @example
1355
+ * ```typescript
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);
1362
+ * ```
1363
+ */
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;
1393
+ } catch (error) {
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 });
1502
+ throw error;
1503
+ }
1504
+ }
1505
+ };
1506
+
1507
+ // src/orders/client.ts
1508
+ var OrderClient = class {
1810
1509
  /**
1811
- * Gets the current price for a token.
1510
+ * Creates a new order client instance.
1511
+ *
1512
+ * @param config - Order client configuration
1513
+ */
1514
+ constructor(config) {
1515
+ this.httpClient = config.httpClient;
1516
+ this.wallet = config.wallet;
1517
+ this.logger = config.logger || new NoOpLogger();
1518
+ this.orderSigner = new OrderSigner(config.wallet, this.logger);
1519
+ this.marketFetcher = config.marketFetcher || new MarketFetcher(config.httpClient, this.logger);
1520
+ if (config.signingConfig) {
1521
+ this.signingConfig = config.signingConfig;
1522
+ } else {
1523
+ const chainId = parseInt(process.env.CHAIN_ID || "8453");
1524
+ const contractAddress = ZERO_ADDRESS;
1525
+ this.signingConfig = {
1526
+ chainId,
1527
+ contractAddress
1528
+ };
1529
+ this.logger.info("Auto-configured signing (contract address from venue)", {
1530
+ chainId
1531
+ });
1532
+ }
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
+ }
1565
+ /**
1566
+ * Creates and submits a new order.
1567
+ *
1568
+ * @remarks
1569
+ * This method handles the complete order creation flow:
1570
+ * 1. Resolve venue address (from cache or API)
1571
+ * 2. Build unsigned order
1572
+ * 3. Sign with EIP-712 using venue.exchange as verifyingContract
1573
+ * 4. Submit to API
1812
1574
  *
1813
- * @param tokenId - Token ID
1814
- * @returns Promise resolving to price information
1815
- * @throws Error if API request fails
1575
+ * Performance best practice: Always call marketFetcher.getMarket(marketSlug)
1576
+ * before createOrder() to cache venue data and avoid additional API requests.
1577
+ *
1578
+ * @param params - Order parameters
1579
+ * @returns Promise resolving to order response
1580
+ *
1581
+ * @throws Error if order creation fails or venue not found
1816
1582
  *
1817
1583
  * @example
1818
1584
  * ```typescript
1819
- * const price = await fetcher.getPrice('123456');
1820
- * console.log(`Current price: ${price.price}`);
1585
+ * // Best practice: fetch market first to cache venue
1586
+ * const market = await marketFetcher.getMarket('bitcoin-2024');
1587
+ *
1588
+ * const order = await orderClient.createOrder({
1589
+ * tokenId: market.tokens.yes,
1590
+ * price: 0.65,
1591
+ * size: 100,
1592
+ * side: Side.BUY,
1593
+ * orderType: OrderType.GTC,
1594
+ * marketSlug: 'bitcoin-2024'
1595
+ * });
1596
+ *
1597
+ * console.log(`Order created: ${order.order.id}`);
1821
1598
  * ```
1822
1599
  */
1823
- async getPrice(tokenId) {
1824
- this.logger.debug("Fetching price", { tokenId });
1825
- try {
1826
- const price = await this.httpClient.get(`/prices/${tokenId}`);
1827
- this.logger.info("Price fetched successfully", {
1828
- tokenId,
1829
- price: price.price
1830
- });
1831
- return price;
1832
- } catch (error) {
1833
- this.logger.error("Failed to fetch price", error, { tokenId });
1834
- throw error;
1600
+ async createOrder(params) {
1601
+ const userData = await this.ensureUserData();
1602
+ this.logger.info("Creating order", {
1603
+ side: params.side,
1604
+ orderType: params.orderType,
1605
+ marketSlug: params.marketSlug
1606
+ });
1607
+ let venue = this.marketFetcher.getVenue(params.marketSlug);
1608
+ if (!venue) {
1609
+ this.logger.warn(
1610
+ "Venue not cached, fetching market details. For better performance, call marketFetcher.getMarket() before createOrder().",
1611
+ { marketSlug: params.marketSlug }
1612
+ );
1613
+ const market = await this.marketFetcher.getMarket(params.marketSlug);
1614
+ if (!market.venue) {
1615
+ throw new Error(
1616
+ `Market ${params.marketSlug} does not have venue information. Venue data is required for order signing.`
1617
+ );
1618
+ }
1619
+ venue = market.venue;
1835
1620
  }
1621
+ const dynamicSigningConfig = {
1622
+ ...this.signingConfig,
1623
+ contractAddress: venue.exchange
1624
+ };
1625
+ this.logger.debug("Using venue for order signing", {
1626
+ marketSlug: params.marketSlug,
1627
+ exchange: venue.exchange,
1628
+ adapter: venue.adapter
1629
+ });
1630
+ const unsignedOrder = this.orderBuilder.buildOrder(params);
1631
+ this.logger.debug("Built unsigned order", {
1632
+ salt: unsignedOrder.salt,
1633
+ makerAmount: unsignedOrder.makerAmount,
1634
+ takerAmount: unsignedOrder.takerAmount
1635
+ });
1636
+ const signature = await this.orderSigner.signOrder(unsignedOrder, dynamicSigningConfig);
1637
+ const payload = {
1638
+ order: {
1639
+ ...unsignedOrder,
1640
+ signature
1641
+ },
1642
+ orderType: params.orderType,
1643
+ marketSlug: params.marketSlug,
1644
+ ownerId: userData.userId
1645
+ };
1646
+ this.logger.debug("Submitting order to API", payload);
1647
+ const apiResponse = await this.httpClient.post("/orders", payload);
1648
+ this.logger.info("Order created successfully", {
1649
+ orderId: apiResponse.order.id
1650
+ });
1651
+ return this.transformOrderResponse(apiResponse);
1836
1652
  }
1837
- };
1838
-
1839
- // src/portfolio/fetcher.ts
1840
- var PortfolioFetcher = class {
1841
1653
  /**
1842
- * Creates a new portfolio fetcher instance.
1843
- *
1844
- * @param httpClient - Authenticated HTTP client for API requests
1845
- * @param logger - Optional logger for debugging (default: no logging)
1654
+ * Transforms raw API response to clean OrderResponse DTO.
1846
1655
  *
1847
- * @example
1848
- * ```typescript
1849
- * // Create authenticated client
1850
- * const httpClient = new HttpClient({ baseURL: API_URL });
1851
- * await authenticator.authenticate({ client: 'eoa' });
1656
+ * @param apiResponse - Raw API response with nested objects
1657
+ * @returns Clean OrderResponse with only essential fields
1852
1658
  *
1853
- * // Create portfolio fetcher
1854
- * const portfolioFetcher = new PortfolioFetcher(httpClient);
1855
- * ```
1659
+ * @internal
1856
1660
  */
1857
- constructor(httpClient, logger) {
1858
- this.httpClient = httpClient;
1859
- this.logger = logger || new NoOpLogger();
1661
+ transformOrderResponse(apiResponse) {
1662
+ const cleanOrder = {
1663
+ order: {
1664
+ id: apiResponse.order.id,
1665
+ createdAt: apiResponse.order.createdAt,
1666
+ makerAmount: apiResponse.order.makerAmount,
1667
+ takerAmount: apiResponse.order.takerAmount,
1668
+ expiration: apiResponse.order.expiration,
1669
+ signatureType: apiResponse.order.signatureType,
1670
+ salt: apiResponse.order.salt,
1671
+ maker: apiResponse.order.maker,
1672
+ signer: apiResponse.order.signer,
1673
+ taker: apiResponse.order.taker,
1674
+ tokenId: apiResponse.order.tokenId,
1675
+ side: apiResponse.order.side,
1676
+ feeRateBps: apiResponse.order.feeRateBps,
1677
+ nonce: apiResponse.order.nonce,
1678
+ signature: apiResponse.order.signature,
1679
+ orderType: apiResponse.order.orderType,
1680
+ price: apiResponse.order.price,
1681
+ marketId: apiResponse.order.marketId
1682
+ }
1683
+ };
1684
+ if (apiResponse.makerMatches && apiResponse.makerMatches.length > 0) {
1685
+ cleanOrder.makerMatches = apiResponse.makerMatches.map((match) => ({
1686
+ id: match.id,
1687
+ createdAt: match.createdAt,
1688
+ matchedSize: match.matchedSize,
1689
+ orderId: match.orderId
1690
+ }));
1691
+ }
1692
+ return cleanOrder;
1860
1693
  }
1861
1694
  /**
1862
- * Gets raw portfolio positions response from API.
1695
+ * Cancels an existing order by ID.
1863
1696
  *
1864
- * @returns Promise resolving to portfolio positions response with CLOB and AMM positions
1865
- * @throws Error if API request fails or user is not authenticated
1697
+ * @param orderId - Order ID to cancel
1698
+ * @returns Promise resolving to cancellation message
1699
+ *
1700
+ * @throws Error if cancellation fails
1866
1701
  *
1867
1702
  * @example
1868
1703
  * ```typescript
1869
- * const response = await portfolioFetcher.getPositions();
1870
- * console.log(`CLOB positions: ${response.clob.length}`);
1871
- * console.log(`AMM positions: ${response.amm.length}`);
1872
- * console.log(`Total points: ${response.accumulativePoints}`);
1704
+ * const result = await orderClient.cancel('order-id-123');
1705
+ * console.log(result.message); // "Order canceled successfully"
1873
1706
  * ```
1874
1707
  */
1875
- async getPositions() {
1876
- this.logger.debug("Fetching user positions");
1877
- try {
1878
- const response = await this.httpClient.get(
1879
- "/portfolio/positions"
1880
- );
1881
- this.logger.info("Positions fetched successfully", {
1882
- clobCount: response.clob?.length || 0,
1883
- ammCount: response.amm?.length || 0
1884
- });
1885
- return response;
1886
- } catch (error) {
1887
- this.logger.error("Failed to fetch positions", error);
1888
- throw error;
1889
- }
1708
+ async cancel(orderId) {
1709
+ this.logger.info("Cancelling order", { orderId });
1710
+ const response = await this.httpClient.delete(`/orders/${orderId}`);
1711
+ this.logger.info("Order cancellation response", {
1712
+ orderId,
1713
+ message: response.message
1714
+ });
1715
+ return response;
1890
1716
  }
1891
1717
  /**
1892
- * Gets CLOB positions only.
1718
+ * Cancels all orders for a specific market.
1893
1719
  *
1894
- * @returns Promise resolving to array of CLOB positions
1895
- * @throws Error if API request fails
1720
+ * @param marketSlug - Market slug to cancel all orders for
1721
+ * @returns Promise resolving to cancellation message
1722
+ *
1723
+ * @throws Error if cancellation fails
1896
1724
  *
1897
1725
  * @example
1898
1726
  * ```typescript
1899
- * const clobPositions = await portfolioFetcher.getCLOBPositions();
1900
- * clobPositions.forEach(pos => {
1901
- * console.log(`${pos.market.title}: YES ${pos.positions.yes.unrealizedPnl} P&L`);
1902
- * });
1727
+ * const result = await orderClient.cancelAll('market-slug-123');
1728
+ * console.log(result.message); // "Orders canceled successfully"
1903
1729
  * ```
1904
1730
  */
1905
- async getCLOBPositions() {
1906
- const response = await this.getPositions();
1907
- return response.clob || [];
1731
+ async cancelAll(marketSlug) {
1732
+ this.logger.info("Cancelling all orders for market", { marketSlug });
1733
+ const response = await this.httpClient.delete(`/orders/all/${marketSlug}`);
1734
+ this.logger.info("All orders cancellation response", {
1735
+ marketSlug,
1736
+ message: response.message
1737
+ });
1738
+ return response;
1908
1739
  }
1909
1740
  /**
1910
- * Gets AMM positions only.
1741
+ * Builds an unsigned order without submitting.
1911
1742
  *
1912
- * @returns Promise resolving to array of AMM positions
1913
- * @throws Error if API request fails
1743
+ * @remarks
1744
+ * Useful for advanced use cases where you need the unsigned order
1745
+ * before signing and submission.
1746
+ *
1747
+ * @param params - Order parameters
1748
+ * @returns Promise resolving to unsigned order
1914
1749
  *
1915
1750
  * @example
1916
1751
  * ```typescript
1917
- * const ammPositions = await portfolioFetcher.getAMMPositions();
1918
- * ammPositions.forEach(pos => {
1919
- * console.log(`${pos.market.title}: ${pos.unrealizedPnl} P&L`);
1752
+ * const unsignedOrder = await orderClient.buildUnsignedOrder({
1753
+ * tokenId: '123456',
1754
+ * price: 0.65,
1755
+ * size: 100,
1756
+ * side: Side.BUY
1920
1757
  * });
1921
1758
  * ```
1922
1759
  */
1923
- async getAMMPositions() {
1924
- const response = await this.getPositions();
1925
- return response.amm || [];
1760
+ async buildUnsignedOrder(params) {
1761
+ await this.ensureUserData();
1762
+ return this.orderBuilder.buildOrder(params);
1926
1763
  }
1927
1764
  /**
1928
- * Flattens positions into a unified format for easier consumption.
1765
+ * Signs an unsigned order without submitting.
1929
1766
  *
1930
1767
  * @remarks
1931
- * Converts CLOB positions (which have YES/NO sides) and AMM positions
1932
- * into a unified Position array. Only includes positions with non-zero values.
1768
+ * Useful for advanced use cases where you need to inspect
1769
+ * the signature before submission.
1933
1770
  *
1934
- * @returns Promise resolving to array of flattened positions
1935
- * @throws Error if API request fails
1771
+ * @param order - Unsigned order to sign
1772
+ * @returns Promise resolving to signature
1936
1773
  *
1937
1774
  * @example
1938
1775
  * ```typescript
1939
- * const positions = await portfolioFetcher.getFlattenedPositions();
1940
- * positions.forEach(pos => {
1941
- * const pnlPercent = (pos.unrealizedPnl / pos.costBasis) * 100;
1942
- * console.log(`${pos.market.title} (${pos.side}): ${pnlPercent.toFixed(2)}% P&L`);
1943
- * });
1776
+ * const signature = await orderClient.signOrder(unsignedOrder);
1944
1777
  * ```
1945
1778
  */
1946
- async getFlattenedPositions() {
1947
- const response = await this.getPositions();
1948
- const positions = [];
1949
- for (const clobPos of response.clob || []) {
1950
- const yesCost = parseFloat(clobPos.positions.yes.cost);
1951
- const yesValue = parseFloat(clobPos.positions.yes.marketValue);
1952
- if (yesCost > 0 || yesValue > 0) {
1953
- positions.push({
1954
- type: "CLOB",
1955
- market: clobPos.market,
1956
- side: "YES",
1957
- costBasis: yesCost,
1958
- marketValue: yesValue,
1959
- unrealizedPnl: parseFloat(clobPos.positions.yes.unrealizedPnl),
1960
- realizedPnl: parseFloat(clobPos.positions.yes.realisedPnl),
1961
- currentPrice: clobPos.latestTrade?.latestYesPrice ?? 0,
1962
- avgPrice: yesCost > 0 ? parseFloat(clobPos.positions.yes.fillPrice) / 1e6 : 0,
1963
- tokenBalance: parseFloat(clobPos.tokensBalance.yes)
1964
- });
1965
- }
1966
- const noCost = parseFloat(clobPos.positions.no.cost);
1967
- const noValue = parseFloat(clobPos.positions.no.marketValue);
1968
- if (noCost > 0 || noValue > 0) {
1969
- positions.push({
1970
- type: "CLOB",
1971
- market: clobPos.market,
1972
- side: "NO",
1973
- costBasis: noCost,
1974
- marketValue: noValue,
1975
- unrealizedPnl: parseFloat(clobPos.positions.no.unrealizedPnl),
1976
- realizedPnl: parseFloat(clobPos.positions.no.realisedPnl),
1977
- currentPrice: clobPos.latestTrade?.latestNoPrice ?? 0,
1978
- avgPrice: noCost > 0 ? parseFloat(clobPos.positions.no.fillPrice) / 1e6 : 0,
1979
- tokenBalance: parseFloat(clobPos.tokensBalance.no)
1980
- });
1981
- }
1982
- }
1983
- for (const ammPos of response.amm || []) {
1984
- const cost = parseFloat(ammPos.totalBuysCost);
1985
- const value = parseFloat(ammPos.collateralAmount);
1986
- if (cost > 0 || value > 0) {
1987
- positions.push({
1988
- type: "AMM",
1989
- market: ammPos.market,
1990
- side: ammPos.outcomeIndex === 0 ? "YES" : "NO",
1991
- costBasis: cost,
1992
- marketValue: value,
1993
- unrealizedPnl: parseFloat(ammPos.unrealizedPnl),
1994
- realizedPnl: parseFloat(ammPos.realizedPnl),
1995
- currentPrice: ammPos.latestTrade ? parseFloat(ammPos.latestTrade.outcomeTokenPrice) : 0,
1996
- avgPrice: parseFloat(ammPos.averageFillPrice),
1997
- tokenBalance: parseFloat(ammPos.outcomeTokenAmount)
1998
- });
1999
- }
2000
- }
2001
- this.logger.debug("Flattened positions", { count: positions.length });
2002
- return positions;
1779
+ async signOrder(order) {
1780
+ return await this.orderSigner.signOrder(order, this.signingConfig);
2003
1781
  }
2004
1782
  /**
2005
- * Calculates portfolio summary statistics from raw API response.
1783
+ * Gets the wallet address.
2006
1784
  *
2007
- * @param response - Portfolio positions response from API
2008
- * @returns Portfolio summary with totals and statistics
1785
+ * @returns Ethereum address of the wallet
2009
1786
  *
2010
1787
  * @example
2011
1788
  * ```typescript
2012
- * const response = await portfolioFetcher.getPositions();
2013
- * const summary = portfolioFetcher.calculateSummary(response);
2014
- *
2015
- * console.log(`Total Portfolio Value: $${(summary.totalValue / 1e6).toFixed(2)}`);
2016
- * console.log(`Total P&L: ${summary.totalUnrealizedPnlPercent.toFixed(2)}%`);
2017
- * console.log(`CLOB Positions: ${summary.breakdown.clob.positions}`);
2018
- * console.log(`AMM Positions: ${summary.breakdown.amm.positions}`);
1789
+ * const address = orderClient.walletAddress;
1790
+ * console.log(`Wallet: ${address}`);
2019
1791
  * ```
2020
1792
  */
2021
- calculateSummary(response) {
2022
- this.logger.debug("Calculating portfolio summary", {
2023
- clobCount: response.clob?.length || 0,
2024
- ammCount: response.amm?.length || 0
2025
- });
2026
- let totalValue = 0;
2027
- let totalCostBasis = 0;
2028
- let totalUnrealizedPnl = 0;
2029
- let totalRealizedPnl = 0;
2030
- let clobPositions = 0;
2031
- let clobValue = 0;
2032
- let clobPnl = 0;
2033
- let ammPositions = 0;
2034
- let ammValue = 0;
2035
- let ammPnl = 0;
2036
- for (const clobPos of response.clob || []) {
2037
- const yesCost = parseFloat(clobPos.positions.yes.cost);
2038
- const yesValue = parseFloat(clobPos.positions.yes.marketValue);
2039
- const yesUnrealizedPnl = parseFloat(clobPos.positions.yes.unrealizedPnl);
2040
- const yesRealizedPnl = parseFloat(clobPos.positions.yes.realisedPnl);
2041
- if (yesCost > 0 || yesValue > 0) {
2042
- clobPositions++;
2043
- totalCostBasis += yesCost;
2044
- totalValue += yesValue;
2045
- totalUnrealizedPnl += yesUnrealizedPnl;
2046
- totalRealizedPnl += yesRealizedPnl;
2047
- clobValue += yesValue;
2048
- clobPnl += yesUnrealizedPnl;
2049
- }
2050
- const noCost = parseFloat(clobPos.positions.no.cost);
2051
- const noValue = parseFloat(clobPos.positions.no.marketValue);
2052
- const noUnrealizedPnl = parseFloat(clobPos.positions.no.unrealizedPnl);
2053
- const noRealizedPnl = parseFloat(clobPos.positions.no.realisedPnl);
2054
- if (noCost > 0 || noValue > 0) {
2055
- clobPositions++;
2056
- totalCostBasis += noCost;
2057
- totalValue += noValue;
2058
- totalUnrealizedPnl += noUnrealizedPnl;
2059
- totalRealizedPnl += noRealizedPnl;
2060
- clobValue += noValue;
2061
- clobPnl += noUnrealizedPnl;
2062
- }
2063
- }
2064
- for (const ammPos of response.amm || []) {
2065
- const cost = parseFloat(ammPos.totalBuysCost);
2066
- const value = parseFloat(ammPos.collateralAmount);
2067
- const unrealizedPnl = parseFloat(ammPos.unrealizedPnl);
2068
- const realizedPnl = parseFloat(ammPos.realizedPnl);
2069
- if (cost > 0 || value > 0) {
2070
- ammPositions++;
2071
- totalCostBasis += cost;
2072
- totalValue += value;
2073
- totalUnrealizedPnl += unrealizedPnl;
2074
- totalRealizedPnl += realizedPnl;
2075
- ammValue += value;
2076
- ammPnl += unrealizedPnl;
2077
- }
2078
- }
2079
- const totalUnrealizedPnlPercent = totalCostBasis > 0 ? totalUnrealizedPnl / totalCostBasis * 100 : 0;
2080
- const uniqueMarkets = /* @__PURE__ */ new Set();
2081
- for (const pos of response.clob || []) {
2082
- uniqueMarkets.add(pos.market.id);
2083
- }
2084
- for (const pos of response.amm || []) {
2085
- uniqueMarkets.add(pos.market.id);
2086
- }
2087
- const summary = {
2088
- totalValue,
2089
- totalCostBasis,
2090
- totalUnrealizedPnl,
2091
- totalRealizedPnl,
2092
- totalUnrealizedPnlPercent,
2093
- positionCount: clobPositions + ammPositions,
2094
- marketCount: uniqueMarkets.size,
2095
- breakdown: {
2096
- clob: {
2097
- positions: clobPositions,
2098
- value: clobValue,
2099
- pnl: clobPnl
2100
- },
2101
- amm: {
2102
- positions: ammPositions,
2103
- value: ammValue,
2104
- pnl: ammPnl
2105
- }
2106
- }
2107
- };
2108
- this.logger.debug("Portfolio summary calculated", summary);
2109
- return summary;
1793
+ get walletAddress() {
1794
+ return this.wallet.address;
2110
1795
  }
2111
1796
  /**
2112
- * Gets positions and calculates summary in a single call.
1797
+ * Gets the owner ID (user ID from profile).
2113
1798
  *
2114
- * @returns Promise resolving to response and summary
2115
- * @throws Error if API request fails or user is not authenticated
1799
+ * @returns Owner ID from user profile, or undefined if not yet loaded
2116
1800
  *
2117
1801
  * @example
2118
1802
  * ```typescript
2119
- * const { response, summary } = await portfolioFetcher.getPortfolio();
2120
- *
2121
- * console.log('Portfolio Summary:');
2122
- * console.log(` Total Value: $${(summary.totalValue / 1e6).toFixed(2)}`);
2123
- * console.log(` Total P&L: $${(summary.totalUnrealizedPnl / 1e6).toFixed(2)}`);
2124
- * console.log(` P&L %: ${summary.totalUnrealizedPnlPercent.toFixed(2)}%`);
2125
- * console.log(`\nCLOB Positions: ${response.clob.length}`);
2126
- * console.log(`AMM Positions: ${response.amm.length}`);
1803
+ * const ownerId = orderClient.ownerId;
1804
+ * if (ownerId) {
1805
+ * console.log(`Owner ID: ${ownerId}`);
1806
+ * }
2127
1807
  * ```
2128
1808
  */
2129
- async getPortfolio() {
2130
- this.logger.debug("Fetching portfolio with summary");
2131
- const response = await this.getPositions();
2132
- const summary = this.calculateSummary(response);
2133
- this.logger.info("Portfolio fetched with summary", {
2134
- positionCount: summary.positionCount,
2135
- totalValueUSDC: summary.totalValue / 1e6,
2136
- pnlPercent: summary.totalUnrealizedPnlPercent
2137
- });
2138
- return { response, summary };
1809
+ get ownerId() {
1810
+ return this.cachedUserData?.userId;
2139
1811
  }
2140
1812
  };
2141
1813
 
@@ -2156,7 +1828,7 @@ var WebSocketClient = class {
2156
1828
  this.pendingListeners = [];
2157
1829
  this.config = {
2158
1830
  url: config.url || DEFAULT_WS_URL,
2159
- sessionCookie: config.sessionCookie || "",
1831
+ apiKey: config.apiKey || process.env.LIMITLESS_API_KEY || "",
2160
1832
  autoReconnect: config.autoReconnect ?? true,
2161
1833
  reconnectDelay: config.reconnectDelay || 1e3,
2162
1834
  maxReconnectAttempts: config.maxReconnectAttempts || Infinity,
@@ -2181,18 +1853,29 @@ var WebSocketClient = class {
2181
1853
  return this.state === "connected" /* CONNECTED */ && this.socket?.connected === true;
2182
1854
  }
2183
1855
  /**
2184
- * Sets the session cookie for authentication.
1856
+ * Sets the API key for authentication.
2185
1857
  *
2186
- * @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.
2187
1863
  */
2188
- setSessionCookie(sessionCookie) {
2189
- this.config.sessionCookie = sessionCookie;
1864
+ setApiKey(apiKey) {
1865
+ this.config.apiKey = apiKey;
2190
1866
  if (this.socket?.connected) {
2191
- this.logger.info("Session cookie updated, reconnecting...");
2192
- this.disconnect();
2193
- this.connect();
1867
+ this.logger.info("API key updated, reconnecting...");
1868
+ this.reconnectWithNewAuth();
2194
1869
  }
2195
1870
  }
1871
+ /**
1872
+ * Reconnects with new authentication credentials.
1873
+ * @internal
1874
+ */
1875
+ async reconnectWithNewAuth() {
1876
+ await this.disconnect();
1877
+ await this.connect();
1878
+ }
2196
1879
  /**
2197
1880
  * Connects to the WebSocket server.
2198
1881
  *
@@ -2206,8 +1889,8 @@ var WebSocketClient = class {
2206
1889
  * ```
2207
1890
  */
2208
1891
  async connect() {
2209
- if (this.socket?.connected) {
2210
- this.logger.info("Already connected");
1892
+ if (this.socket?.connected || this.state === "connecting" /* CONNECTING */) {
1893
+ this.logger.info("Already connected or connecting");
2211
1894
  return;
2212
1895
  }
2213
1896
  this.logger.info("Connecting to WebSocket", { url: this.config.url });
@@ -2216,18 +1899,26 @@ var WebSocketClient = class {
2216
1899
  const timeout = setTimeout(() => {
2217
1900
  reject(new Error(`Connection timeout after ${this.config.timeout}ms`));
2218
1901
  }, this.config.timeout);
2219
- const wsUrl = this.config.url.endsWith("/markets") ? this.config.url : `${this.config.url}/markets`;
2220
- this.socket = io(wsUrl, {
1902
+ const wsUrl = this.config.url;
1903
+ const socketOptions = {
2221
1904
  transports: ["websocket"],
2222
1905
  // Use WebSocket transport only
2223
- extraHeaders: {
2224
- cookie: `limitless_session=${this.config.sessionCookie}`
2225
- },
2226
1906
  reconnection: this.config.autoReconnect,
2227
1907
  reconnectionDelay: this.config.reconnectDelay,
2228
- 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
2229
1914
  timeout: this.config.timeout
2230
- });
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);
2231
1922
  this.attachPendingListeners();
2232
1923
  this.setupEventHandlers();
2233
1924
  this.socket.once("connect", () => {
@@ -2249,12 +1940,14 @@ var WebSocketClient = class {
2249
1940
  /**
2250
1941
  * Disconnects from the WebSocket server.
2251
1942
  *
1943
+ * @returns Promise that resolves when disconnected
1944
+ *
2252
1945
  * @example
2253
1946
  * ```typescript
2254
- * wsClient.disconnect();
1947
+ * await wsClient.disconnect();
2255
1948
  * ```
2256
1949
  */
2257
- disconnect() {
1950
+ async disconnect() {
2258
1951
  if (!this.socket) {
2259
1952
  return;
2260
1953
  }
@@ -2269,13 +1962,13 @@ var WebSocketClient = class {
2269
1962
  *
2270
1963
  * @param channel - Channel to subscribe to
2271
1964
  * @param options - Subscription options
2272
- * @returns Promise that resolves when subscribed
1965
+ * @returns Promise that resolves immediately (kept async for API compatibility)
2273
1966
  * @throws Error if not connected
2274
1967
  *
2275
1968
  * @example
2276
1969
  * ```typescript
2277
1970
  * // Subscribe to orderbook for a specific market
2278
- * await wsClient.subscribe('orderbook', { marketSlug: 'market-123' });
1971
+ * await wsClient.subscribe('orderbook', { marketSlugs: ['market-123'] });
2279
1972
  *
2280
1973
  * // Subscribe to all trades
2281
1974
  * await wsClient.subscribe('trades');
@@ -2288,21 +1981,20 @@ var WebSocketClient = class {
2288
1981
  if (!this.isConnected()) {
2289
1982
  throw new Error("WebSocket not connected. Call connect() first.");
2290
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
+ }
2291
1993
  const subscriptionKey = this.getSubscriptionKey(channel, options);
2292
1994
  this.subscriptions.set(subscriptionKey, options);
2293
1995
  this.logger.info("Subscribing to channel", { channel, options });
2294
- return new Promise((resolve, reject) => {
2295
- this.socket.emit(channel, options, (response) => {
2296
- if (response?.error) {
2297
- this.logger.error("Subscription failed", response.error);
2298
- this.subscriptions.delete(subscriptionKey);
2299
- reject(new Error(response.error));
2300
- } else {
2301
- this.logger.info("Subscribed successfully", { channel, options });
2302
- resolve();
2303
- }
2304
- });
2305
- });
1996
+ this.socket.emit(channel, options);
1997
+ this.logger.info("Subscription request sent", { channel, options });
2306
1998
  }
2307
1999
  /**
2308
2000
  * Unsubscribes from a channel.
@@ -2310,10 +2002,11 @@ var WebSocketClient = class {
2310
2002
  * @param channel - Channel to unsubscribe from
2311
2003
  * @param options - Subscription options (must match subscribe call)
2312
2004
  * @returns Promise that resolves when unsubscribed
2005
+ * @throws Error if not connected or unsubscribe fails
2313
2006
  *
2314
2007
  * @example
2315
2008
  * ```typescript
2316
- * await wsClient.unsubscribe('orderbook', { marketSlug: 'market-123' });
2009
+ * await wsClient.unsubscribe('orderbook', { marketSlugs: ['market-123'] });
2317
2010
  * ```
2318
2011
  */
2319
2012
  async unsubscribe(channel, options = {}) {
@@ -2323,17 +2016,19 @@ var WebSocketClient = class {
2323
2016
  const subscriptionKey = this.getSubscriptionKey(channel, options);
2324
2017
  this.subscriptions.delete(subscriptionKey);
2325
2018
  this.logger.info("Unsubscribing from channel", { channel, options });
2326
- return new Promise((resolve, reject) => {
2327
- this.socket.emit("unsubscribe", { channel, ...options }, (response) => {
2328
- if (response?.error) {
2329
- this.logger.error("Unsubscribe failed", response.error);
2330
- reject(new Error(response.error));
2331
- } else {
2332
- this.logger.info("Unsubscribed successfully", { channel, options });
2333
- resolve();
2334
- }
2335
- });
2336
- });
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
+ }
2337
2032
  }
2338
2033
  /**
2339
2034
  * Registers an event listener.
@@ -2376,14 +2071,27 @@ var WebSocketClient = class {
2376
2071
  * Removes an event listener.
2377
2072
  *
2378
2073
  * @param event - Event name
2379
- * @param handler - Event handler to remove
2074
+ * @param handler - Event handler to remove (if undefined, removes all handlers for event)
2380
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
+ * ```
2381
2085
  */
2382
2086
  off(event, handler) {
2383
2087
  if (!this.socket) {
2384
2088
  return this;
2385
2089
  }
2386
- this.socket.off(event, handler);
2090
+ if (handler === void 0) {
2091
+ this.socket.removeAllListeners(event);
2092
+ } else {
2093
+ this.socket.off(event, handler);
2094
+ }
2387
2095
  return this;
2388
2096
  }
2389
2097
  /**
@@ -2475,8 +2183,7 @@ var WebSocketClient = class {
2475
2183
  };
2476
2184
  export {
2477
2185
  APIError,
2478
- AuthenticatedClient,
2479
- Authenticator,
2186
+ AuthenticationError,
2480
2187
  BASE_SEPOLIA_CHAIN_ID,
2481
2188
  CONTRACT_ADDRESSES,
2482
2189
  ConsoleLogger,
@@ -2484,15 +2191,16 @@ export {
2484
2191
  DEFAULT_CHAIN_ID,
2485
2192
  DEFAULT_WS_URL,
2486
2193
  HttpClient,
2194
+ Market,
2487
2195
  MarketFetcher,
2488
- MarketType,
2489
- MessageSigner,
2490
2196
  NoOpLogger,
2491
2197
  OrderBuilder,
2492
2198
  OrderClient,
2493
2199
  OrderSigner,
2494
2200
  OrderType,
2201
+ OrderValidationError,
2495
2202
  PortfolioFetcher,
2203
+ RateLimitError,
2496
2204
  RetryConfig,
2497
2205
  RetryableClient,
2498
2206
  SIGNING_MESSAGE_TEMPLATE,