@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/README.md +154 -74
- package/dist/index.d.mts +905 -1022
- package/dist/index.d.ts +905 -1022
- package/dist/index.js +477 -833
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +473 -830
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -31,8 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
APIError: () => APIError,
|
|
34
|
-
|
|
35
|
-
Authenticator: () => Authenticator,
|
|
34
|
+
AuthenticationError: () => AuthenticationError,
|
|
36
35
|
BASE_SEPOLIA_CHAIN_ID: () => BASE_SEPOLIA_CHAIN_ID,
|
|
37
36
|
CONTRACT_ADDRESSES: () => CONTRACT_ADDRESSES,
|
|
38
37
|
ConsoleLogger: () => ConsoleLogger,
|
|
@@ -40,14 +39,16 @@ __export(index_exports, {
|
|
|
40
39
|
DEFAULT_CHAIN_ID: () => DEFAULT_CHAIN_ID,
|
|
41
40
|
DEFAULT_WS_URL: () => DEFAULT_WS_URL,
|
|
42
41
|
HttpClient: () => HttpClient,
|
|
42
|
+
Market: () => Market,
|
|
43
43
|
MarketFetcher: () => MarketFetcher,
|
|
44
|
-
MessageSigner: () => MessageSigner,
|
|
45
44
|
NoOpLogger: () => NoOpLogger,
|
|
46
45
|
OrderBuilder: () => OrderBuilder,
|
|
47
46
|
OrderClient: () => OrderClient,
|
|
48
47
|
OrderSigner: () => OrderSigner,
|
|
49
48
|
OrderType: () => OrderType,
|
|
49
|
+
OrderValidationError: () => OrderValidationError,
|
|
50
50
|
PortfolioFetcher: () => PortfolioFetcher,
|
|
51
|
+
RateLimitError: () => RateLimitError,
|
|
51
52
|
RetryConfig: () => RetryConfig,
|
|
52
53
|
RetryableClient: () => RetryableClient,
|
|
53
54
|
SIGNING_MESSAGE_TEMPLATE: () => SIGNING_MESSAGE_TEMPLATE,
|
|
@@ -136,250 +137,92 @@ var WebSocketState = /* @__PURE__ */ ((WebSocketState2) => {
|
|
|
136
137
|
return WebSocketState2;
|
|
137
138
|
})(WebSocketState || {});
|
|
138
139
|
|
|
139
|
-
// src/
|
|
140
|
-
var
|
|
141
|
-
var MessageSigner = class {
|
|
142
|
-
/**
|
|
143
|
-
* Creates a new message signer instance.
|
|
144
|
-
*
|
|
145
|
-
* @param wallet - Ethers wallet instance for signing
|
|
146
|
-
*/
|
|
147
|
-
constructor(wallet) {
|
|
148
|
-
this.wallet = wallet;
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Creates authentication headers for API requests.
|
|
152
|
-
*
|
|
153
|
-
* @param signingMessage - Message to sign from the API
|
|
154
|
-
* @returns Promise resolving to signature headers
|
|
155
|
-
*
|
|
156
|
-
* @example
|
|
157
|
-
* ```typescript
|
|
158
|
-
* const signer = new MessageSigner(wallet);
|
|
159
|
-
* const headers = await signer.createAuthHeaders(message);
|
|
160
|
-
* ```
|
|
161
|
-
*/
|
|
162
|
-
async createAuthHeaders(signingMessage) {
|
|
163
|
-
const hexMessage = this.stringToHex(signingMessage);
|
|
164
|
-
const signature = await this.wallet.signMessage(signingMessage);
|
|
165
|
-
const address = this.wallet.address;
|
|
166
|
-
const recoveredAddress = import_ethers.ethers.verifyMessage(signingMessage, signature);
|
|
167
|
-
if (address.toLowerCase() !== recoveredAddress.toLowerCase()) {
|
|
168
|
-
throw new Error("Signature verification failed: address mismatch");
|
|
169
|
-
}
|
|
170
|
-
return {
|
|
171
|
-
"x-account": address,
|
|
172
|
-
"x-signing-message": hexMessage,
|
|
173
|
-
"x-signature": signature
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Signs EIP-712 typed data.
|
|
178
|
-
*
|
|
179
|
-
* @param domain - EIP-712 domain
|
|
180
|
-
* @param types - EIP-712 types
|
|
181
|
-
* @param value - Value to sign
|
|
182
|
-
* @returns Promise resolving to signature string
|
|
183
|
-
*
|
|
184
|
-
* @example
|
|
185
|
-
* ```typescript
|
|
186
|
-
* const signature = await signer.signTypedData(domain, types, order);
|
|
187
|
-
* ```
|
|
188
|
-
*/
|
|
189
|
-
async signTypedData(domain, types, value) {
|
|
190
|
-
return await this.wallet.signTypedData(domain, types, value);
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Gets the wallet address.
|
|
194
|
-
*
|
|
195
|
-
* @returns Ethereum address
|
|
196
|
-
*/
|
|
197
|
-
getAddress() {
|
|
198
|
-
return this.wallet.address;
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Converts a string to hex format.
|
|
202
|
-
*
|
|
203
|
-
* @param text - String to convert
|
|
204
|
-
* @returns Hex string with 0x prefix
|
|
205
|
-
* @internal
|
|
206
|
-
*/
|
|
207
|
-
stringToHex(text) {
|
|
208
|
-
return import_ethers.ethers.hexlify(import_ethers.ethers.toUtf8Bytes(text));
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
// src/auth/authenticator.ts
|
|
213
|
-
var Authenticator = class {
|
|
140
|
+
// src/types/market-class.ts
|
|
141
|
+
var Market = class _Market {
|
|
214
142
|
/**
|
|
215
|
-
* Creates a
|
|
216
|
-
*
|
|
217
|
-
* @param httpClient - HTTP client for API requests
|
|
218
|
-
* @param signer - Message signer for wallet operations
|
|
219
|
-
* @param logger - Optional logger for debugging and monitoring (default: no logging)
|
|
143
|
+
* Creates a Market instance.
|
|
220
144
|
*
|
|
221
|
-
* @
|
|
222
|
-
*
|
|
223
|
-
* // Without logging (default)
|
|
224
|
-
* const authenticator = new Authenticator(httpClient, signer);
|
|
225
|
-
*
|
|
226
|
-
* // With logging
|
|
227
|
-
* import { ConsoleLogger } from '@limitless-exchange/sdk';
|
|
228
|
-
* const logger = new ConsoleLogger('debug');
|
|
229
|
-
* const authenticator = new Authenticator(httpClient, signer, logger);
|
|
230
|
-
* ```
|
|
145
|
+
* @param data - Market data from API
|
|
146
|
+
* @param httpClient - HTTP client for making requests
|
|
231
147
|
*/
|
|
232
|
-
constructor(
|
|
148
|
+
constructor(data, httpClient) {
|
|
149
|
+
Object.assign(this, data);
|
|
233
150
|
this.httpClient = httpClient;
|
|
234
|
-
|
|
235
|
-
|
|
151
|
+
if (data.markets && Array.isArray(data.markets)) {
|
|
152
|
+
this.markets = data.markets.map((m) => new _Market(m, httpClient));
|
|
153
|
+
}
|
|
236
154
|
}
|
|
237
155
|
/**
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
* @returns Promise resolving to signing message string
|
|
241
|
-
* @throws Error if API request fails
|
|
156
|
+
* Get user's orders for this market.
|
|
242
157
|
*
|
|
243
|
-
* @
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
* console.log(message);
|
|
247
|
-
* ```
|
|
248
|
-
*/
|
|
249
|
-
async getSigningMessage() {
|
|
250
|
-
this.logger.debug("Requesting signing message from API");
|
|
251
|
-
const message = await this.httpClient.get("/auth/signing-message");
|
|
252
|
-
this.logger.debug("Received signing message", { length: message.length });
|
|
253
|
-
return message;
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* Authenticates with the API and obtains session cookie.
|
|
158
|
+
* @remarks
|
|
159
|
+
* Fetches all orders placed by the authenticated user for this specific market.
|
|
160
|
+
* Uses the http_client from the MarketFetcher that created this Market instance.
|
|
257
161
|
*
|
|
258
|
-
* @
|
|
259
|
-
* @
|
|
260
|
-
* @throws Error if authentication fails or smart wallet is required but not provided
|
|
162
|
+
* @returns Promise resolving to array of user orders
|
|
163
|
+
* @throws Error if market wasn't fetched via MarketFetcher
|
|
261
164
|
*
|
|
262
165
|
* @example
|
|
263
166
|
* ```typescript
|
|
264
|
-
* //
|
|
265
|
-
* const
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
* const result = await authenticator.authenticate({
|
|
269
|
-
* client: 'etherspot',
|
|
270
|
-
* smartWallet: '0x...'
|
|
271
|
-
* });
|
|
167
|
+
* // Clean fluent API
|
|
168
|
+
* const market = await marketFetcher.getMarket("bitcoin-2024");
|
|
169
|
+
* const orders = await market.getUserOrders();
|
|
170
|
+
* console.log(`You have ${orders.length} orders in ${market.title}`);
|
|
272
171
|
* ```
|
|
273
172
|
*/
|
|
274
|
-
async
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
hasSmartWallet: !!options.smartWallet
|
|
279
|
-
});
|
|
280
|
-
if (client === "etherspot" && !options.smartWallet) {
|
|
281
|
-
this.logger.error("Smart wallet address required for ETHERSPOT client");
|
|
282
|
-
throw new Error("Smart wallet address is required for ETHERSPOT client");
|
|
283
|
-
}
|
|
284
|
-
try {
|
|
285
|
-
const signingMessage = await this.getSigningMessage();
|
|
286
|
-
this.logger.debug("Creating signature headers");
|
|
287
|
-
const headers = await this.signer.createAuthHeaders(signingMessage);
|
|
288
|
-
this.logger.debug("Sending authentication request", { client });
|
|
289
|
-
const response = await this.httpClient.postWithResponse(
|
|
290
|
-
"/auth/login",
|
|
291
|
-
{ client, smartWallet: options.smartWallet },
|
|
292
|
-
{
|
|
293
|
-
headers,
|
|
294
|
-
validateStatus: (status) => status < 500
|
|
295
|
-
}
|
|
173
|
+
async getUserOrders() {
|
|
174
|
+
if (!this.httpClient) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
"This Market instance has no httpClient attached. Make sure to fetch the market via MarketFetcher.getMarket() to use this method."
|
|
296
177
|
);
|
|
297
|
-
this.logger.debug("Extracting session cookie from response");
|
|
298
|
-
const cookies = this.httpClient.extractCookies(response);
|
|
299
|
-
const sessionCookie = cookies["limitless_session"];
|
|
300
|
-
if (!sessionCookie) {
|
|
301
|
-
this.logger.error("Session cookie not found in response headers");
|
|
302
|
-
throw new Error("Failed to obtain session cookie from response");
|
|
303
|
-
}
|
|
304
|
-
this.httpClient.setSessionCookie(sessionCookie);
|
|
305
|
-
this.logger.info("Authentication successful", {
|
|
306
|
-
account: response.data.account,
|
|
307
|
-
client: response.data.client
|
|
308
|
-
});
|
|
309
|
-
return {
|
|
310
|
-
sessionCookie,
|
|
311
|
-
profile: response.data
|
|
312
|
-
};
|
|
313
|
-
} catch (error) {
|
|
314
|
-
this.logger.error("Authentication failed", error, {
|
|
315
|
-
client
|
|
316
|
-
});
|
|
317
|
-
throw error;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
/**
|
|
321
|
-
* Verifies the current authentication status.
|
|
322
|
-
*
|
|
323
|
-
* @param sessionCookie - Session cookie to verify
|
|
324
|
-
* @returns Promise resolving to user's Ethereum address
|
|
325
|
-
* @throws Error if session is invalid
|
|
326
|
-
*
|
|
327
|
-
* @example
|
|
328
|
-
* ```typescript
|
|
329
|
-
* const address = await authenticator.verifyAuth(sessionCookie);
|
|
330
|
-
* console.log(`Authenticated as: ${address}`);
|
|
331
|
-
* ```
|
|
332
|
-
*/
|
|
333
|
-
async verifyAuth(sessionCookie) {
|
|
334
|
-
this.logger.debug("Verifying authentication session");
|
|
335
|
-
const originalCookie = this.httpClient["sessionCookie"];
|
|
336
|
-
this.httpClient.setSessionCookie(sessionCookie);
|
|
337
|
-
try {
|
|
338
|
-
const address = await this.httpClient.get("/auth/verify-auth");
|
|
339
|
-
this.logger.info("Session verified", { address });
|
|
340
|
-
return address;
|
|
341
|
-
} catch (error) {
|
|
342
|
-
this.logger.error("Session verification failed", error);
|
|
343
|
-
throw error;
|
|
344
|
-
} finally {
|
|
345
|
-
if (originalCookie) {
|
|
346
|
-
this.httpClient.setSessionCookie(originalCookie);
|
|
347
|
-
} else {
|
|
348
|
-
this.httpClient.clearSessionCookie();
|
|
349
|
-
}
|
|
350
178
|
}
|
|
179
|
+
const response = await this.httpClient.get(`/markets/${this.slug}/user-orders`);
|
|
180
|
+
const orders = Array.isArray(response) ? response : response.orders || [];
|
|
181
|
+
return orders;
|
|
351
182
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
this.httpClient.clearSessionCookie();
|
|
379
|
-
}
|
|
380
|
-
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// src/api/http.ts
|
|
186
|
+
var import_axios = __toESM(require("axios"));
|
|
187
|
+
var import_http = __toESM(require("http"));
|
|
188
|
+
var import_https = __toESM(require("https"));
|
|
189
|
+
|
|
190
|
+
// src/utils/constants.ts
|
|
191
|
+
var DEFAULT_API_URL = "https://api.limitless.exchange";
|
|
192
|
+
var DEFAULT_WS_URL = "wss://ws.limitless.exchange";
|
|
193
|
+
var DEFAULT_CHAIN_ID = 8453;
|
|
194
|
+
var BASE_SEPOLIA_CHAIN_ID = 84532;
|
|
195
|
+
var SIGNING_MESSAGE_TEMPLATE = "Welcome to Limitless.exchange! Please sign this message to verify your identity.\n\nNonce: {NONCE}";
|
|
196
|
+
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
197
|
+
var CONTRACT_ADDRESSES = {
|
|
198
|
+
// Base mainnet (chainId: 8453)
|
|
199
|
+
8453: {
|
|
200
|
+
USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
201
|
+
// Native USDC on Base
|
|
202
|
+
CTF: "0xC9c98965297Bc527861c898329Ee280632B76e18"
|
|
203
|
+
// Conditional Token Framework
|
|
204
|
+
},
|
|
205
|
+
// Base Sepolia testnet (chainId: 84532)
|
|
206
|
+
84532: {
|
|
207
|
+
USDC: "0x...",
|
|
208
|
+
CTF: "0x..."
|
|
381
209
|
}
|
|
382
210
|
};
|
|
211
|
+
function getContractAddress(contractType, chainId = DEFAULT_CHAIN_ID) {
|
|
212
|
+
const addresses = CONTRACT_ADDRESSES[chainId];
|
|
213
|
+
if (!addresses) {
|
|
214
|
+
throw new Error(
|
|
215
|
+
`No contract addresses configured for chainId ${chainId}. Supported chains: ${Object.keys(CONTRACT_ADDRESSES).join(", ")}`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
const address = addresses[contractType];
|
|
219
|
+
if (!address || address === "0x...") {
|
|
220
|
+
throw new Error(
|
|
221
|
+
`Contract address for ${contractType} not available on chainId ${chainId}. Please configure the address in constants.ts or use environment variables.`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
return address;
|
|
225
|
+
}
|
|
383
226
|
|
|
384
227
|
// src/api/errors.ts
|
|
385
228
|
var APIError = class _APIError extends Error {
|
|
@@ -425,118 +268,33 @@ var APIError = class _APIError extends Error {
|
|
|
425
268
|
return this.status === 401 || this.status === 403;
|
|
426
269
|
}
|
|
427
270
|
};
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
* @param config - Configuration for authenticated client
|
|
435
|
-
*/
|
|
436
|
-
constructor(config) {
|
|
437
|
-
this.httpClient = config.httpClient;
|
|
438
|
-
this.authenticator = config.authenticator;
|
|
439
|
-
this.client = config.client;
|
|
440
|
-
this.smartWallet = config.smartWallet;
|
|
441
|
-
this.logger = config.logger || new NoOpLogger();
|
|
442
|
-
this.maxRetries = config.maxRetries ?? 1;
|
|
443
|
-
}
|
|
444
|
-
/**
|
|
445
|
-
* Executes a function with automatic retry on authentication errors.
|
|
446
|
-
*
|
|
447
|
-
* @param fn - Function to execute with auth retry
|
|
448
|
-
* @returns Promise resolving to the function result
|
|
449
|
-
* @throws Error if max retries exceeded or non-auth error occurs
|
|
450
|
-
*
|
|
451
|
-
* @example
|
|
452
|
-
* ```typescript
|
|
453
|
-
* // Automatic retry on 401/403
|
|
454
|
-
* const positions = await authClient.withRetry(() =>
|
|
455
|
-
* portfolioFetcher.getPositions()
|
|
456
|
-
* );
|
|
457
|
-
*
|
|
458
|
-
* // Works with any async operation
|
|
459
|
-
* const order = await authClient.withRetry(() =>
|
|
460
|
-
* orderClient.createOrder({ ... })
|
|
461
|
-
* );
|
|
462
|
-
* ```
|
|
463
|
-
*/
|
|
464
|
-
async withRetry(fn) {
|
|
465
|
-
let attempts = 0;
|
|
466
|
-
while (attempts <= this.maxRetries) {
|
|
467
|
-
try {
|
|
468
|
-
return await fn();
|
|
469
|
-
} catch (error) {
|
|
470
|
-
if (error instanceof APIError && error.isAuthError() && attempts < this.maxRetries) {
|
|
471
|
-
this.logger.info("Authentication expired, re-authenticating...", {
|
|
472
|
-
attempt: attempts + 1,
|
|
473
|
-
maxRetries: this.maxRetries
|
|
474
|
-
});
|
|
475
|
-
await this.reauthenticate();
|
|
476
|
-
attempts++;
|
|
477
|
-
continue;
|
|
478
|
-
}
|
|
479
|
-
throw error;
|
|
480
|
-
}
|
|
271
|
+
var RateLimitError = class _RateLimitError extends APIError {
|
|
272
|
+
constructor(message = "Rate limit exceeded", status = 429, data = null, url, method) {
|
|
273
|
+
super(message, status, data, url, method);
|
|
274
|
+
this.name = "RateLimitError";
|
|
275
|
+
if (Error.captureStackTrace) {
|
|
276
|
+
Error.captureStackTrace(this, _RateLimitError);
|
|
481
277
|
}
|
|
482
|
-
throw new Error("Unexpected error: exceeded max retries");
|
|
483
|
-
}
|
|
484
|
-
/**
|
|
485
|
-
* Re-authenticates with the API.
|
|
486
|
-
*
|
|
487
|
-
* @internal
|
|
488
|
-
*/
|
|
489
|
-
async reauthenticate() {
|
|
490
|
-
this.logger.debug("Re-authenticating with API");
|
|
491
|
-
await this.authenticator.authenticate({
|
|
492
|
-
client: this.client,
|
|
493
|
-
smartWallet: this.smartWallet
|
|
494
|
-
});
|
|
495
|
-
this.logger.info("Re-authentication successful");
|
|
496
278
|
}
|
|
497
279
|
};
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
var DEFAULT_API_URL = "https://api.limitless.exchange";
|
|
506
|
-
var DEFAULT_WS_URL = "wss://ws.limitless.exchange";
|
|
507
|
-
var DEFAULT_CHAIN_ID = 8453;
|
|
508
|
-
var BASE_SEPOLIA_CHAIN_ID = 84532;
|
|
509
|
-
var SIGNING_MESSAGE_TEMPLATE = "Welcome to Limitless.exchange! Please sign this message to verify your identity.\n\nNonce: {NONCE}";
|
|
510
|
-
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
511
|
-
var CONTRACT_ADDRESSES = {
|
|
512
|
-
// Base mainnet (chainId: 8453)
|
|
513
|
-
8453: {
|
|
514
|
-
USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
515
|
-
// Native USDC on Base
|
|
516
|
-
CTF: "0xC9c98965297Bc527861c898329Ee280632B76e18"
|
|
517
|
-
// Conditional Token Framework
|
|
518
|
-
},
|
|
519
|
-
// Base Sepolia testnet (chainId: 84532)
|
|
520
|
-
84532: {
|
|
521
|
-
USDC: "0x...",
|
|
522
|
-
CTF: "0x..."
|
|
280
|
+
var AuthenticationError = class _AuthenticationError extends APIError {
|
|
281
|
+
constructor(message = "Authentication failed", status = 401, data = null, url, method) {
|
|
282
|
+
super(message, status, data, url, method);
|
|
283
|
+
this.name = "AuthenticationError";
|
|
284
|
+
if (Error.captureStackTrace) {
|
|
285
|
+
Error.captureStackTrace(this, _AuthenticationError);
|
|
286
|
+
}
|
|
523
287
|
}
|
|
524
288
|
};
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
const address = addresses[contractType];
|
|
533
|
-
if (!address || address === "0x...") {
|
|
534
|
-
throw new Error(
|
|
535
|
-
`Contract address for ${contractType} not available on chainId ${chainId}. Please configure the address in constants.ts or use environment variables.`
|
|
536
|
-
);
|
|
289
|
+
var ValidationError = class _ValidationError extends APIError {
|
|
290
|
+
constructor(message, status = 400, data = null, url, method) {
|
|
291
|
+
super(message, status, data, url, method);
|
|
292
|
+
this.name = "ValidationError";
|
|
293
|
+
if (Error.captureStackTrace) {
|
|
294
|
+
Error.captureStackTrace(this, _ValidationError);
|
|
295
|
+
}
|
|
537
296
|
}
|
|
538
|
-
|
|
539
|
-
}
|
|
297
|
+
};
|
|
540
298
|
|
|
541
299
|
// src/api/http.ts
|
|
542
300
|
var HttpClient = class {
|
|
@@ -546,8 +304,13 @@ var HttpClient = class {
|
|
|
546
304
|
* @param config - Configuration options for the HTTP client
|
|
547
305
|
*/
|
|
548
306
|
constructor(config = {}) {
|
|
549
|
-
this.
|
|
307
|
+
this.apiKey = config.apiKey || process.env.LIMITLESS_API_KEY;
|
|
550
308
|
this.logger = config.logger || new NoOpLogger();
|
|
309
|
+
if (!this.apiKey) {
|
|
310
|
+
this.logger.warn(
|
|
311
|
+
"API key not set. Authenticated endpoints will fail. Set LIMITLESS_API_KEY environment variable or pass apiKey parameter."
|
|
312
|
+
);
|
|
313
|
+
}
|
|
551
314
|
const keepAlive = config.keepAlive !== false;
|
|
552
315
|
const maxSockets = config.maxSockets || 50;
|
|
553
316
|
const maxFreeSockets = config.maxFreeSockets || 10;
|
|
@@ -590,13 +353,17 @@ var HttpClient = class {
|
|
|
590
353
|
setupInterceptors() {
|
|
591
354
|
this.client.interceptors.request.use(
|
|
592
355
|
(config) => {
|
|
593
|
-
if (this.
|
|
594
|
-
config.headers["
|
|
356
|
+
if (this.apiKey) {
|
|
357
|
+
config.headers["X-API-Key"] = this.apiKey;
|
|
595
358
|
}
|
|
596
359
|
const fullUrl = `${config.baseURL || ""}${config.url || ""}`;
|
|
597
360
|
const method = config.method?.toUpperCase() || "GET";
|
|
361
|
+
const logHeaders = { ...config.headers };
|
|
362
|
+
if (logHeaders["X-API-Key"]) {
|
|
363
|
+
logHeaders["X-API-Key"] = "***";
|
|
364
|
+
}
|
|
598
365
|
this.logger.debug(`\u2192 ${method} ${fullUrl}`, {
|
|
599
|
-
headers:
|
|
366
|
+
headers: logHeaders,
|
|
600
367
|
body: config.data
|
|
601
368
|
});
|
|
602
369
|
return config;
|
|
@@ -637,7 +404,15 @@ var HttpClient = class {
|
|
|
637
404
|
message = String(data);
|
|
638
405
|
}
|
|
639
406
|
}
|
|
640
|
-
|
|
407
|
+
if (status === 429) {
|
|
408
|
+
throw new RateLimitError(message, status, data, url, method);
|
|
409
|
+
} else if (status === 401 || status === 403) {
|
|
410
|
+
throw new AuthenticationError(message, status, data, url, method);
|
|
411
|
+
} else if (status === 400) {
|
|
412
|
+
throw new ValidationError(message, status, data, url, method);
|
|
413
|
+
} else {
|
|
414
|
+
throw new APIError(message, status, data, url, method);
|
|
415
|
+
}
|
|
641
416
|
} else if (error.request) {
|
|
642
417
|
throw new Error("No response received from API");
|
|
643
418
|
} else {
|
|
@@ -647,18 +422,18 @@ var HttpClient = class {
|
|
|
647
422
|
);
|
|
648
423
|
}
|
|
649
424
|
/**
|
|
650
|
-
* Sets the
|
|
425
|
+
* Sets the API key for authenticated requests.
|
|
651
426
|
*
|
|
652
|
-
* @param
|
|
427
|
+
* @param apiKey - API key value
|
|
653
428
|
*/
|
|
654
|
-
|
|
655
|
-
this.
|
|
429
|
+
setApiKey(apiKey) {
|
|
430
|
+
this.apiKey = apiKey;
|
|
656
431
|
}
|
|
657
432
|
/**
|
|
658
|
-
* Clears the
|
|
433
|
+
* Clears the API key.
|
|
659
434
|
*/
|
|
660
|
-
|
|
661
|
-
this.
|
|
435
|
+
clearApiKey() {
|
|
436
|
+
this.apiKey = void 0;
|
|
662
437
|
}
|
|
663
438
|
/**
|
|
664
439
|
* Performs a GET request.
|
|
@@ -683,19 +458,6 @@ var HttpClient = class {
|
|
|
683
458
|
const response = await this.client.post(url, data, config);
|
|
684
459
|
return response.data;
|
|
685
460
|
}
|
|
686
|
-
/**
|
|
687
|
-
* Performs a POST request and returns the full response object.
|
|
688
|
-
* Useful when you need access to response headers (e.g., for cookie extraction).
|
|
689
|
-
*
|
|
690
|
-
* @param url - Request URL
|
|
691
|
-
* @param data - Request body data
|
|
692
|
-
* @param config - Additional request configuration
|
|
693
|
-
* @returns Promise resolving to the full AxiosResponse object
|
|
694
|
-
* @internal
|
|
695
|
-
*/
|
|
696
|
-
async postWithResponse(url, data, config) {
|
|
697
|
-
return await this.client.post(url, data, config);
|
|
698
|
-
}
|
|
699
461
|
/**
|
|
700
462
|
* Performs a DELETE request.
|
|
701
463
|
*
|
|
@@ -718,26 +480,6 @@ var HttpClient = class {
|
|
|
718
480
|
const response = await this.client.delete(url, deleteConfig);
|
|
719
481
|
return response.data;
|
|
720
482
|
}
|
|
721
|
-
/**
|
|
722
|
-
* Extracts cookies from response headers.
|
|
723
|
-
*
|
|
724
|
-
* @param response - Axios response object
|
|
725
|
-
* @returns Object containing parsed cookies
|
|
726
|
-
* @internal
|
|
727
|
-
*/
|
|
728
|
-
extractCookies(response) {
|
|
729
|
-
const setCookie = response.headers["set-cookie"];
|
|
730
|
-
if (!setCookie) return {};
|
|
731
|
-
const cookies = {};
|
|
732
|
-
const cookieStrings = Array.isArray(setCookie) ? setCookie : [setCookie];
|
|
733
|
-
for (const cookieString of cookieStrings) {
|
|
734
|
-
const parts = cookieString.split(";")[0].split("=");
|
|
735
|
-
if (parts.length === 2) {
|
|
736
|
-
cookies[parts[0].trim()] = parts[1].trim();
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
return cookies;
|
|
740
|
-
}
|
|
741
483
|
};
|
|
742
484
|
|
|
743
485
|
// src/api/retry.ts
|
|
@@ -922,7 +664,7 @@ var RetryableClient = class {
|
|
|
922
664
|
};
|
|
923
665
|
|
|
924
666
|
// src/orders/builder.ts
|
|
925
|
-
var
|
|
667
|
+
var import_ethers = require("ethers");
|
|
926
668
|
var ZERO_ADDRESS2 = "0x0000000000000000000000000000000000000000";
|
|
927
669
|
var DEFAULT_PRICE_TICK = 1e-3;
|
|
928
670
|
var OrderBuilder = class {
|
|
@@ -1004,7 +746,7 @@ var OrderBuilder = class {
|
|
|
1004
746
|
* @internal
|
|
1005
747
|
*/
|
|
1006
748
|
isFOKOrder(args) {
|
|
1007
|
-
return "
|
|
749
|
+
return "makerAmount" in args;
|
|
1008
750
|
}
|
|
1009
751
|
/**
|
|
1010
752
|
* Generates a unique salt using timestamp + nano-offset pattern.
|
|
@@ -1158,7 +900,7 @@ var OrderBuilder = class {
|
|
|
1158
900
|
}
|
|
1159
901
|
}
|
|
1160
902
|
const amountFormatted = makerAmount.toFixed(DECIMALS);
|
|
1161
|
-
const amountScaled =
|
|
903
|
+
const amountScaled = import_ethers.ethers.parseUnits(amountFormatted, DECIMALS);
|
|
1162
904
|
const collateralAmount = Number(amountScaled);
|
|
1163
905
|
return {
|
|
1164
906
|
makerAmount: collateralAmount,
|
|
@@ -1178,7 +920,7 @@ var OrderBuilder = class {
|
|
|
1178
920
|
if (!args.tokenId || args.tokenId === "0") {
|
|
1179
921
|
throw new Error("Invalid tokenId: tokenId is required.");
|
|
1180
922
|
}
|
|
1181
|
-
if (args.taker && !
|
|
923
|
+
if (args.taker && !import_ethers.ethers.isAddress(args.taker)) {
|
|
1182
924
|
throw new Error(`Invalid taker address: ${args.taker}`);
|
|
1183
925
|
}
|
|
1184
926
|
if (this.isFOKOrder(args)) {
|
|
@@ -1189,7 +931,7 @@ var OrderBuilder = class {
|
|
|
1189
931
|
throw new Error(`Invalid makerAmount: ${args.makerAmount}. Maker amount must be positive.`);
|
|
1190
932
|
}
|
|
1191
933
|
} else {
|
|
1192
|
-
if (args.price < 0 || args.price > 1) {
|
|
934
|
+
if (args.price == null || args.price < 0 || args.price > 1) {
|
|
1193
935
|
throw new Error(`Invalid price: ${args.price}. Price must be between 0 and 1.`);
|
|
1194
936
|
}
|
|
1195
937
|
if (args.size <= 0) {
|
|
@@ -1278,11 +1020,11 @@ var OrderSigner = class {
|
|
|
1278
1020
|
signatureType: order.signatureType
|
|
1279
1021
|
};
|
|
1280
1022
|
this.logger.debug("EIP-712 Order Value", orderValue);
|
|
1281
|
-
|
|
1023
|
+
this.logger.debug("Full signing payload", {
|
|
1282
1024
|
domain,
|
|
1283
1025
|
types: this.getTypes(),
|
|
1284
1026
|
value: orderValue
|
|
1285
|
-
}
|
|
1027
|
+
});
|
|
1286
1028
|
try {
|
|
1287
1029
|
const signature = await this.wallet.signTypedData(domain, types, orderValue);
|
|
1288
1030
|
this.logger.info("Successfully generated EIP-712 signature", {
|
|
@@ -1342,11 +1084,11 @@ var OrderSigner = class {
|
|
|
1342
1084
|
};
|
|
1343
1085
|
|
|
1344
1086
|
// src/orders/validator.ts
|
|
1345
|
-
var
|
|
1346
|
-
var
|
|
1087
|
+
var import_ethers2 = require("ethers");
|
|
1088
|
+
var OrderValidationError = class extends Error {
|
|
1347
1089
|
constructor(message) {
|
|
1348
1090
|
super(message);
|
|
1349
|
-
this.name = "
|
|
1091
|
+
this.name = "OrderValidationError";
|
|
1350
1092
|
}
|
|
1351
1093
|
};
|
|
1352
1094
|
function isFOKOrder(args) {
|
|
@@ -1354,126 +1096,126 @@ function isFOKOrder(args) {
|
|
|
1354
1096
|
}
|
|
1355
1097
|
function validateOrderArgs(args) {
|
|
1356
1098
|
if (!args.tokenId) {
|
|
1357
|
-
throw new
|
|
1099
|
+
throw new OrderValidationError("TokenId is required");
|
|
1358
1100
|
}
|
|
1359
1101
|
if (args.tokenId === "0") {
|
|
1360
|
-
throw new
|
|
1102
|
+
throw new OrderValidationError("TokenId cannot be zero");
|
|
1361
1103
|
}
|
|
1362
1104
|
if (!/^\d+$/.test(args.tokenId)) {
|
|
1363
|
-
throw new
|
|
1105
|
+
throw new OrderValidationError(`Invalid tokenId format: ${args.tokenId}`);
|
|
1364
1106
|
}
|
|
1365
|
-
if (args.taker && !
|
|
1366
|
-
throw new
|
|
1107
|
+
if (args.taker && !import_ethers2.ethers.isAddress(args.taker)) {
|
|
1108
|
+
throw new OrderValidationError(`Invalid taker address: ${args.taker}`);
|
|
1367
1109
|
}
|
|
1368
1110
|
if (args.expiration !== void 0) {
|
|
1369
1111
|
if (!/^\d+$/.test(args.expiration)) {
|
|
1370
|
-
throw new
|
|
1112
|
+
throw new OrderValidationError(`Invalid expiration format: ${args.expiration}`);
|
|
1371
1113
|
}
|
|
1372
1114
|
}
|
|
1373
1115
|
if (args.nonce !== void 0) {
|
|
1374
1116
|
if (!Number.isInteger(args.nonce) || args.nonce < 0) {
|
|
1375
|
-
throw new
|
|
1117
|
+
throw new OrderValidationError(`Invalid nonce: ${args.nonce}`);
|
|
1376
1118
|
}
|
|
1377
1119
|
}
|
|
1378
1120
|
if (isFOKOrder(args)) {
|
|
1379
1121
|
if (typeof args.makerAmount !== "number" || isNaN(args.makerAmount)) {
|
|
1380
|
-
throw new
|
|
1122
|
+
throw new OrderValidationError("Amount must be a valid number");
|
|
1381
1123
|
}
|
|
1382
1124
|
if (args.makerAmount <= 0) {
|
|
1383
|
-
throw new
|
|
1125
|
+
throw new OrderValidationError(`Amount must be positive, got: ${args.makerAmount}`);
|
|
1384
1126
|
}
|
|
1385
1127
|
const amountStr = args.makerAmount.toString();
|
|
1386
1128
|
const decimalIndex = amountStr.indexOf(".");
|
|
1387
1129
|
if (decimalIndex !== -1) {
|
|
1388
1130
|
const decimalPlaces = amountStr.length - decimalIndex - 1;
|
|
1389
1131
|
if (decimalPlaces > 2) {
|
|
1390
|
-
throw new
|
|
1132
|
+
throw new OrderValidationError(
|
|
1391
1133
|
`Amount must have max 2 decimal places, got: ${args.makerAmount} (${decimalPlaces} decimals)`
|
|
1392
1134
|
);
|
|
1393
1135
|
}
|
|
1394
1136
|
}
|
|
1395
1137
|
} else {
|
|
1396
1138
|
if (typeof args.price !== "number" || isNaN(args.price)) {
|
|
1397
|
-
throw new
|
|
1139
|
+
throw new OrderValidationError("Price must be a valid number");
|
|
1398
1140
|
}
|
|
1399
1141
|
if (args.price < 0 || args.price > 1) {
|
|
1400
|
-
throw new
|
|
1142
|
+
throw new OrderValidationError(`Price must be between 0 and 1, got: ${args.price}`);
|
|
1401
1143
|
}
|
|
1402
1144
|
if (typeof args.size !== "number" || isNaN(args.size)) {
|
|
1403
|
-
throw new
|
|
1145
|
+
throw new OrderValidationError("Size must be a valid number");
|
|
1404
1146
|
}
|
|
1405
1147
|
if (args.size <= 0) {
|
|
1406
|
-
throw new
|
|
1148
|
+
throw new OrderValidationError(`Size must be positive, got: ${args.size}`);
|
|
1407
1149
|
}
|
|
1408
1150
|
}
|
|
1409
1151
|
}
|
|
1410
1152
|
function validateUnsignedOrder(order) {
|
|
1411
|
-
if (!
|
|
1412
|
-
throw new
|
|
1153
|
+
if (!import_ethers2.ethers.isAddress(order.maker)) {
|
|
1154
|
+
throw new OrderValidationError(`Invalid maker address: ${order.maker}`);
|
|
1413
1155
|
}
|
|
1414
|
-
if (!
|
|
1415
|
-
throw new
|
|
1156
|
+
if (!import_ethers2.ethers.isAddress(order.signer)) {
|
|
1157
|
+
throw new OrderValidationError(`Invalid signer address: ${order.signer}`);
|
|
1416
1158
|
}
|
|
1417
|
-
if (!
|
|
1418
|
-
throw new
|
|
1159
|
+
if (!import_ethers2.ethers.isAddress(order.taker)) {
|
|
1160
|
+
throw new OrderValidationError(`Invalid taker address: ${order.taker}`);
|
|
1419
1161
|
}
|
|
1420
1162
|
if (!order.makerAmount || order.makerAmount === 0) {
|
|
1421
|
-
throw new
|
|
1163
|
+
throw new OrderValidationError("MakerAmount must be greater than zero");
|
|
1422
1164
|
}
|
|
1423
1165
|
if (!order.takerAmount || order.takerAmount === 0) {
|
|
1424
|
-
throw new
|
|
1166
|
+
throw new OrderValidationError("TakerAmount must be greater than zero");
|
|
1425
1167
|
}
|
|
1426
1168
|
if (typeof order.makerAmount !== "number" || order.makerAmount <= 0) {
|
|
1427
|
-
throw new
|
|
1169
|
+
throw new OrderValidationError(`Invalid makerAmount: ${order.makerAmount}`);
|
|
1428
1170
|
}
|
|
1429
1171
|
if (typeof order.takerAmount !== "number" || order.takerAmount <= 0) {
|
|
1430
|
-
throw new
|
|
1172
|
+
throw new OrderValidationError(`Invalid takerAmount: ${order.takerAmount}`);
|
|
1431
1173
|
}
|
|
1432
1174
|
if (!/^\d+$/.test(order.tokenId)) {
|
|
1433
|
-
throw new
|
|
1175
|
+
throw new OrderValidationError(`Invalid tokenId format: ${order.tokenId}`);
|
|
1434
1176
|
}
|
|
1435
1177
|
if (!/^\d+$/.test(order.expiration)) {
|
|
1436
|
-
throw new
|
|
1178
|
+
throw new OrderValidationError(`Invalid expiration format: ${order.expiration}`);
|
|
1437
1179
|
}
|
|
1438
1180
|
if (!Number.isInteger(order.salt) || order.salt <= 0) {
|
|
1439
|
-
throw new
|
|
1181
|
+
throw new OrderValidationError(`Invalid salt: ${order.salt}`);
|
|
1440
1182
|
}
|
|
1441
1183
|
if (!Number.isInteger(order.nonce) || order.nonce < 0) {
|
|
1442
|
-
throw new
|
|
1184
|
+
throw new OrderValidationError(`Invalid nonce: ${order.nonce}`);
|
|
1443
1185
|
}
|
|
1444
1186
|
if (!Number.isInteger(order.feeRateBps) || order.feeRateBps < 0) {
|
|
1445
|
-
throw new
|
|
1187
|
+
throw new OrderValidationError(`Invalid feeRateBps: ${order.feeRateBps}`);
|
|
1446
1188
|
}
|
|
1447
1189
|
if (order.side !== 0 && order.side !== 1) {
|
|
1448
|
-
throw new
|
|
1190
|
+
throw new OrderValidationError(`Invalid side: ${order.side}. Must be 0 (BUY) or 1 (SELL)`);
|
|
1449
1191
|
}
|
|
1450
1192
|
if (!Number.isInteger(order.signatureType) || order.signatureType < 0) {
|
|
1451
|
-
throw new
|
|
1193
|
+
throw new OrderValidationError(`Invalid signatureType: ${order.signatureType}`);
|
|
1452
1194
|
}
|
|
1453
1195
|
if (order.price !== void 0) {
|
|
1454
1196
|
if (typeof order.price !== "number" || isNaN(order.price)) {
|
|
1455
|
-
throw new
|
|
1197
|
+
throw new OrderValidationError("Price must be a valid number");
|
|
1456
1198
|
}
|
|
1457
1199
|
if (order.price < 0 || order.price > 1) {
|
|
1458
|
-
throw new
|
|
1200
|
+
throw new OrderValidationError(`Price must be between 0 and 1, got: ${order.price}`);
|
|
1459
1201
|
}
|
|
1460
1202
|
}
|
|
1461
1203
|
}
|
|
1462
1204
|
function validateSignedOrder(order) {
|
|
1463
1205
|
validateUnsignedOrder(order);
|
|
1464
1206
|
if (!order.signature) {
|
|
1465
|
-
throw new
|
|
1207
|
+
throw new OrderValidationError("Signature is required");
|
|
1466
1208
|
}
|
|
1467
1209
|
if (!order.signature.startsWith("0x")) {
|
|
1468
|
-
throw new
|
|
1210
|
+
throw new OrderValidationError("Signature must start with 0x");
|
|
1469
1211
|
}
|
|
1470
1212
|
if (order.signature.length !== 132) {
|
|
1471
|
-
throw new
|
|
1213
|
+
throw new OrderValidationError(
|
|
1472
1214
|
`Invalid signature length: ${order.signature.length}. Expected 132 characters.`
|
|
1473
1215
|
);
|
|
1474
1216
|
}
|
|
1475
1217
|
if (!/^0x[0-9a-fA-F]{130}$/.test(order.signature)) {
|
|
1476
|
-
throw new
|
|
1218
|
+
throw new OrderValidationError("Signature must be valid hex string");
|
|
1477
1219
|
}
|
|
1478
1220
|
}
|
|
1479
1221
|
|
|
@@ -1535,13 +1277,18 @@ var MarketFetcher = class {
|
|
|
1535
1277
|
this.logger.debug("Fetching active markets", { params });
|
|
1536
1278
|
try {
|
|
1537
1279
|
const response = await this.httpClient.get(endpoint);
|
|
1280
|
+
const markets = response.data.map((marketData) => new Market(marketData, this.httpClient));
|
|
1281
|
+
const result = {
|
|
1282
|
+
data: markets,
|
|
1283
|
+
totalMarketsCount: response.totalMarketsCount
|
|
1284
|
+
};
|
|
1538
1285
|
this.logger.info("Active markets fetched successfully", {
|
|
1539
|
-
count:
|
|
1286
|
+
count: markets.length,
|
|
1540
1287
|
total: response.totalMarketsCount,
|
|
1541
1288
|
sortBy: params?.sortBy,
|
|
1542
1289
|
page: params?.page
|
|
1543
1290
|
});
|
|
1544
|
-
return
|
|
1291
|
+
return result;
|
|
1545
1292
|
} catch (error) {
|
|
1546
1293
|
this.logger.error("Failed to fetch active markets", error, { params });
|
|
1547
1294
|
throw error;
|
|
@@ -1561,9 +1308,14 @@ var MarketFetcher = class {
|
|
|
1561
1308
|
*
|
|
1562
1309
|
* @example
|
|
1563
1310
|
* ```typescript
|
|
1311
|
+
* // Get market
|
|
1564
1312
|
* const market = await fetcher.getMarket('bitcoin-price-2024');
|
|
1565
1313
|
* console.log(`Market: ${market.title}`);
|
|
1566
1314
|
*
|
|
1315
|
+
* // Fluent API - get user orders for this market (clean!)
|
|
1316
|
+
* const orders = await market.getUserOrders();
|
|
1317
|
+
* console.log(`You have ${orders.length} orders`);
|
|
1318
|
+
*
|
|
1567
1319
|
* // Venue is now cached for order signing
|
|
1568
1320
|
* await orderClient.createOrder({
|
|
1569
1321
|
* marketSlug: 'bitcoin-price-2024',
|
|
@@ -1574,7 +1326,8 @@ var MarketFetcher = class {
|
|
|
1574
1326
|
async getMarket(slug) {
|
|
1575
1327
|
this.logger.debug("Fetching market", { slug });
|
|
1576
1328
|
try {
|
|
1577
|
-
const
|
|
1329
|
+
const response = await this.httpClient.get(`/markets/${slug}`);
|
|
1330
|
+
const market = new Market(response, this.httpClient);
|
|
1578
1331
|
if (market.venue) {
|
|
1579
1332
|
this.venueCache.set(slug, market.venue);
|
|
1580
1333
|
this.logger.debug("Venue cached for order signing", {
|
|
@@ -1657,30 +1410,164 @@ var MarketFetcher = class {
|
|
|
1657
1410
|
throw error;
|
|
1658
1411
|
}
|
|
1659
1412
|
}
|
|
1413
|
+
};
|
|
1414
|
+
|
|
1415
|
+
// src/portfolio/fetcher.ts
|
|
1416
|
+
var PortfolioFetcher = class {
|
|
1660
1417
|
/**
|
|
1661
|
-
*
|
|
1418
|
+
* Creates a new portfolio fetcher instance.
|
|
1662
1419
|
*
|
|
1663
|
-
* @param
|
|
1664
|
-
* @
|
|
1665
|
-
* @throws Error if API request fails
|
|
1420
|
+
* @param httpClient - Authenticated HTTP client for API requests
|
|
1421
|
+
* @param logger - Optional logger for debugging (default: no logging)
|
|
1666
1422
|
*
|
|
1667
1423
|
* @example
|
|
1668
1424
|
* ```typescript
|
|
1669
|
-
*
|
|
1670
|
-
*
|
|
1425
|
+
* // Create authenticated client
|
|
1426
|
+
* const httpClient = new HttpClient({ baseURL: API_URL });
|
|
1427
|
+
* await authenticator.authenticate({ client: 'eoa' });
|
|
1428
|
+
*
|
|
1429
|
+
* // Create portfolio fetcher
|
|
1430
|
+
* const portfolioFetcher = new PortfolioFetcher(httpClient);
|
|
1671
1431
|
* ```
|
|
1672
1432
|
*/
|
|
1673
|
-
|
|
1674
|
-
this.
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1433
|
+
constructor(httpClient, logger) {
|
|
1434
|
+
this.httpClient = httpClient;
|
|
1435
|
+
this.logger = logger || new NoOpLogger();
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* Gets user profile for a specific wallet address.
|
|
1439
|
+
*
|
|
1440
|
+
* @remarks
|
|
1441
|
+
* Returns user profile data including user ID and fee rate.
|
|
1442
|
+
* Used internally by OrderClient to fetch user data.
|
|
1443
|
+
*
|
|
1444
|
+
* @param address - Wallet address to fetch profile for
|
|
1445
|
+
* @returns Promise resolving to user profile data
|
|
1446
|
+
* @throws Error if API request fails or user is not authenticated
|
|
1447
|
+
*
|
|
1448
|
+
* @example
|
|
1449
|
+
* ```typescript
|
|
1450
|
+
* const profile = await portfolioFetcher.getProfile('0x1234...');
|
|
1451
|
+
* console.log(`User ID: ${profile.id}`);
|
|
1452
|
+
* console.log(`Account: ${profile.account}`);
|
|
1453
|
+
* console.log(`Fee Rate: ${profile.rank?.feeRateBps}`);
|
|
1454
|
+
* ```
|
|
1455
|
+
*/
|
|
1456
|
+
async getProfile(address) {
|
|
1457
|
+
this.logger.debug("Fetching user profile", { address });
|
|
1458
|
+
try {
|
|
1459
|
+
const response = await this.httpClient.get(`/profiles/${address}`);
|
|
1460
|
+
this.logger.info("User profile fetched successfully", { address });
|
|
1461
|
+
return response;
|
|
1682
1462
|
} catch (error) {
|
|
1683
|
-
this.logger.error("Failed to fetch
|
|
1463
|
+
this.logger.error("Failed to fetch user profile", error, { address });
|
|
1464
|
+
throw error;
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
/**
|
|
1468
|
+
* Gets raw portfolio positions response from API.
|
|
1469
|
+
*
|
|
1470
|
+
* @returns Promise resolving to portfolio positions response with CLOB and AMM positions
|
|
1471
|
+
* @throws Error if API request fails or user is not authenticated
|
|
1472
|
+
*
|
|
1473
|
+
* @example
|
|
1474
|
+
* ```typescript
|
|
1475
|
+
* const response = await portfolioFetcher.getPositions();
|
|
1476
|
+
* console.log(`CLOB positions: ${response.clob.length}`);
|
|
1477
|
+
* console.log(`AMM positions: ${response.amm.length}`);
|
|
1478
|
+
* console.log(`Total points: ${response.accumulativePoints}`);
|
|
1479
|
+
* ```
|
|
1480
|
+
*/
|
|
1481
|
+
async getPositions() {
|
|
1482
|
+
this.logger.debug("Fetching user positions");
|
|
1483
|
+
try {
|
|
1484
|
+
const response = await this.httpClient.get("/portfolio/positions");
|
|
1485
|
+
this.logger.info("Positions fetched successfully", {
|
|
1486
|
+
clobCount: response.clob?.length || 0,
|
|
1487
|
+
ammCount: response.amm?.length || 0
|
|
1488
|
+
});
|
|
1489
|
+
return response;
|
|
1490
|
+
} catch (error) {
|
|
1491
|
+
this.logger.error("Failed to fetch positions", error);
|
|
1492
|
+
throw error;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
/**
|
|
1496
|
+
* Gets CLOB positions only.
|
|
1497
|
+
*
|
|
1498
|
+
* @returns Promise resolving to array of CLOB positions
|
|
1499
|
+
* @throws Error if API request fails
|
|
1500
|
+
*
|
|
1501
|
+
* @example
|
|
1502
|
+
* ```typescript
|
|
1503
|
+
* const clobPositions = await portfolioFetcher.getCLOBPositions();
|
|
1504
|
+
* clobPositions.forEach(pos => {
|
|
1505
|
+
* console.log(`${pos.market.title}: YES ${pos.positions.yes.unrealizedPnl} P&L`);
|
|
1506
|
+
* });
|
|
1507
|
+
* ```
|
|
1508
|
+
*/
|
|
1509
|
+
async getCLOBPositions() {
|
|
1510
|
+
const response = await this.getPositions();
|
|
1511
|
+
return response.clob || [];
|
|
1512
|
+
}
|
|
1513
|
+
/**
|
|
1514
|
+
* Gets AMM positions only.
|
|
1515
|
+
*
|
|
1516
|
+
* @returns Promise resolving to array of AMM positions
|
|
1517
|
+
* @throws Error if API request fails
|
|
1518
|
+
*
|
|
1519
|
+
* @example
|
|
1520
|
+
* ```typescript
|
|
1521
|
+
* const ammPositions = await portfolioFetcher.getAMMPositions();
|
|
1522
|
+
* ammPositions.forEach(pos => {
|
|
1523
|
+
* console.log(`${pos.market.title}: ${pos.unrealizedPnl} P&L`);
|
|
1524
|
+
* });
|
|
1525
|
+
* ```
|
|
1526
|
+
*/
|
|
1527
|
+
async getAMMPositions() {
|
|
1528
|
+
const response = await this.getPositions();
|
|
1529
|
+
return response.amm || [];
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Gets paginated history of user actions.
|
|
1533
|
+
*
|
|
1534
|
+
* Includes AMM trades, CLOB trades, Negrisk trades & conversions.
|
|
1535
|
+
*
|
|
1536
|
+
* @param page - Page number (starts at 1)
|
|
1537
|
+
* @param limit - Number of items per page
|
|
1538
|
+
* @returns Promise resolving to paginated history response
|
|
1539
|
+
* @throws Error if API request fails or user is not authenticated
|
|
1540
|
+
*
|
|
1541
|
+
* @example
|
|
1542
|
+
* ```typescript
|
|
1543
|
+
* // Get first page
|
|
1544
|
+
* const response = await portfolioFetcher.getUserHistory(1, 20);
|
|
1545
|
+
* console.log(`Found ${response.data.length} of ${response.totalCount} entries`);
|
|
1546
|
+
*
|
|
1547
|
+
* // Process history entries
|
|
1548
|
+
* for (const entry of response.data) {
|
|
1549
|
+
* console.log(`Type: ${entry.type}`);
|
|
1550
|
+
* console.log(`Market: ${entry.marketSlug}`);
|
|
1551
|
+
* }
|
|
1552
|
+
*
|
|
1553
|
+
* // Get next page
|
|
1554
|
+
* const page2 = await portfolioFetcher.getUserHistory(2, 20);
|
|
1555
|
+
* ```
|
|
1556
|
+
*/
|
|
1557
|
+
async getUserHistory(page = 1, limit = 10) {
|
|
1558
|
+
this.logger.debug("Fetching user history", { page, limit });
|
|
1559
|
+
try {
|
|
1560
|
+
const params = new URLSearchParams({
|
|
1561
|
+
page: page.toString(),
|
|
1562
|
+
limit: limit.toString()
|
|
1563
|
+
});
|
|
1564
|
+
const response = await this.httpClient.get(
|
|
1565
|
+
`/portfolio/history?${params.toString()}`
|
|
1566
|
+
);
|
|
1567
|
+
this.logger.info("User history fetched successfully");
|
|
1568
|
+
return response;
|
|
1569
|
+
} catch (error) {
|
|
1570
|
+
this.logger.error("Failed to fetch user history", error, { page, limit });
|
|
1684
1571
|
throw error;
|
|
1685
1572
|
}
|
|
1686
1573
|
}
|
|
@@ -1695,10 +1582,8 @@ var OrderClient = class {
|
|
|
1695
1582
|
*/
|
|
1696
1583
|
constructor(config) {
|
|
1697
1584
|
this.httpClient = config.httpClient;
|
|
1585
|
+
this.wallet = config.wallet;
|
|
1698
1586
|
this.logger = config.logger || new NoOpLogger();
|
|
1699
|
-
this.ownerId = config.userData.userId;
|
|
1700
|
-
const feeRateBps = config.userData.feeRateBps;
|
|
1701
|
-
this.orderBuilder = new OrderBuilder(config.wallet.address, feeRateBps, 1e-3);
|
|
1702
1587
|
this.orderSigner = new OrderSigner(config.wallet, this.logger);
|
|
1703
1588
|
this.marketFetcher = config.marketFetcher || new MarketFetcher(config.httpClient, this.logger);
|
|
1704
1589
|
if (config.signingConfig) {
|
|
@@ -1715,6 +1600,37 @@ var OrderClient = class {
|
|
|
1715
1600
|
});
|
|
1716
1601
|
}
|
|
1717
1602
|
}
|
|
1603
|
+
/**
|
|
1604
|
+
* Ensures user data is loaded and cached.
|
|
1605
|
+
* Fetches from profile API on first call, then caches for subsequent calls.
|
|
1606
|
+
*
|
|
1607
|
+
* @returns Promise resolving to cached user data
|
|
1608
|
+
* @internal
|
|
1609
|
+
*/
|
|
1610
|
+
async ensureUserData() {
|
|
1611
|
+
if (!this.cachedUserData) {
|
|
1612
|
+
this.logger.info("Fetching user profile for order client initialization...", {
|
|
1613
|
+
walletAddress: this.wallet.address
|
|
1614
|
+
});
|
|
1615
|
+
const portfolioFetcher = new PortfolioFetcher(this.httpClient);
|
|
1616
|
+
const profile = await portfolioFetcher.getProfile(this.wallet.address);
|
|
1617
|
+
this.cachedUserData = {
|
|
1618
|
+
userId: profile.id,
|
|
1619
|
+
feeRateBps: profile.rank?.feeRateBps || 300
|
|
1620
|
+
};
|
|
1621
|
+
this.orderBuilder = new OrderBuilder(
|
|
1622
|
+
this.wallet.address,
|
|
1623
|
+
this.cachedUserData.feeRateBps,
|
|
1624
|
+
1e-3
|
|
1625
|
+
);
|
|
1626
|
+
this.logger.info("Order Client initialized", {
|
|
1627
|
+
walletAddress: profile.account,
|
|
1628
|
+
userId: this.cachedUserData.userId,
|
|
1629
|
+
feeRate: `${this.cachedUserData.feeRateBps / 100}%`
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
return this.cachedUserData;
|
|
1633
|
+
}
|
|
1718
1634
|
/**
|
|
1719
1635
|
* Creates and submits a new order.
|
|
1720
1636
|
*
|
|
@@ -1751,6 +1667,7 @@ var OrderClient = class {
|
|
|
1751
1667
|
* ```
|
|
1752
1668
|
*/
|
|
1753
1669
|
async createOrder(params) {
|
|
1670
|
+
const userData = await this.ensureUserData();
|
|
1754
1671
|
this.logger.info("Creating order", {
|
|
1755
1672
|
side: params.side,
|
|
1756
1673
|
orderType: params.orderType,
|
|
@@ -1785,10 +1702,7 @@ var OrderClient = class {
|
|
|
1785
1702
|
makerAmount: unsignedOrder.makerAmount,
|
|
1786
1703
|
takerAmount: unsignedOrder.takerAmount
|
|
1787
1704
|
});
|
|
1788
|
-
const signature = await this.orderSigner.signOrder(
|
|
1789
|
-
unsignedOrder,
|
|
1790
|
-
dynamicSigningConfig
|
|
1791
|
-
);
|
|
1705
|
+
const signature = await this.orderSigner.signOrder(unsignedOrder, dynamicSigningConfig);
|
|
1792
1706
|
const payload = {
|
|
1793
1707
|
order: {
|
|
1794
1708
|
...unsignedOrder,
|
|
@@ -1796,14 +1710,10 @@ var OrderClient = class {
|
|
|
1796
1710
|
},
|
|
1797
1711
|
orderType: params.orderType,
|
|
1798
1712
|
marketSlug: params.marketSlug,
|
|
1799
|
-
ownerId:
|
|
1713
|
+
ownerId: userData.userId
|
|
1800
1714
|
};
|
|
1801
|
-
this.logger.debug("Submitting order to API");
|
|
1802
|
-
|
|
1803
|
-
const apiResponse = await this.httpClient.post(
|
|
1804
|
-
"/orders",
|
|
1805
|
-
payload
|
|
1806
|
-
);
|
|
1715
|
+
this.logger.debug("Submitting order to API", payload);
|
|
1716
|
+
const apiResponse = await this.httpClient.post("/orders", payload);
|
|
1807
1717
|
this.logger.info("Order created successfully", {
|
|
1808
1718
|
orderId: apiResponse.order.id
|
|
1809
1719
|
});
|
|
@@ -1866,9 +1776,7 @@ var OrderClient = class {
|
|
|
1866
1776
|
*/
|
|
1867
1777
|
async cancel(orderId) {
|
|
1868
1778
|
this.logger.info("Cancelling order", { orderId });
|
|
1869
|
-
const response = await this.httpClient.delete(
|
|
1870
|
-
`/orders/${orderId}`
|
|
1871
|
-
);
|
|
1779
|
+
const response = await this.httpClient.delete(`/orders/${orderId}`);
|
|
1872
1780
|
this.logger.info("Order cancellation response", {
|
|
1873
1781
|
orderId,
|
|
1874
1782
|
message: response.message
|
|
@@ -1891,42 +1799,13 @@ var OrderClient = class {
|
|
|
1891
1799
|
*/
|
|
1892
1800
|
async cancelAll(marketSlug) {
|
|
1893
1801
|
this.logger.info("Cancelling all orders for market", { marketSlug });
|
|
1894
|
-
const response = await this.httpClient.delete(
|
|
1895
|
-
`/orders/all/${marketSlug}`
|
|
1896
|
-
);
|
|
1802
|
+
const response = await this.httpClient.delete(`/orders/all/${marketSlug}`);
|
|
1897
1803
|
this.logger.info("All orders cancellation response", {
|
|
1898
1804
|
marketSlug,
|
|
1899
1805
|
message: response.message
|
|
1900
1806
|
});
|
|
1901
1807
|
return response;
|
|
1902
1808
|
}
|
|
1903
|
-
/**
|
|
1904
|
-
* @deprecated Use `cancel()` instead
|
|
1905
|
-
*/
|
|
1906
|
-
async cancelOrder(orderId) {
|
|
1907
|
-
await this.cancel(orderId);
|
|
1908
|
-
}
|
|
1909
|
-
/**
|
|
1910
|
-
* Gets an order by ID.
|
|
1911
|
-
*
|
|
1912
|
-
* @param orderId - Order ID to fetch
|
|
1913
|
-
* @returns Promise resolving to order details
|
|
1914
|
-
*
|
|
1915
|
-
* @throws Error if order not found
|
|
1916
|
-
*
|
|
1917
|
-
* @example
|
|
1918
|
-
* ```typescript
|
|
1919
|
-
* const order = await orderClient.getOrder('order-id-123');
|
|
1920
|
-
* console.log(order.order.side);
|
|
1921
|
-
* ```
|
|
1922
|
-
*/
|
|
1923
|
-
async getOrder(orderId) {
|
|
1924
|
-
this.logger.debug("Fetching order", { orderId });
|
|
1925
|
-
const response = await this.httpClient.get(
|
|
1926
|
-
`/orders/${orderId}`
|
|
1927
|
-
);
|
|
1928
|
-
return response;
|
|
1929
|
-
}
|
|
1930
1809
|
/**
|
|
1931
1810
|
* Builds an unsigned order without submitting.
|
|
1932
1811
|
*
|
|
@@ -1935,11 +1814,11 @@ var OrderClient = class {
|
|
|
1935
1814
|
* before signing and submission.
|
|
1936
1815
|
*
|
|
1937
1816
|
* @param params - Order parameters
|
|
1938
|
-
* @returns
|
|
1817
|
+
* @returns Promise resolving to unsigned order
|
|
1939
1818
|
*
|
|
1940
1819
|
* @example
|
|
1941
1820
|
* ```typescript
|
|
1942
|
-
* const unsignedOrder = orderClient.buildUnsignedOrder({
|
|
1821
|
+
* const unsignedOrder = await orderClient.buildUnsignedOrder({
|
|
1943
1822
|
* tokenId: '123456',
|
|
1944
1823
|
* price: 0.65,
|
|
1945
1824
|
* size: 100,
|
|
@@ -1947,7 +1826,8 @@ var OrderClient = class {
|
|
|
1947
1826
|
* });
|
|
1948
1827
|
* ```
|
|
1949
1828
|
*/
|
|
1950
|
-
buildUnsignedOrder(params) {
|
|
1829
|
+
async buildUnsignedOrder(params) {
|
|
1830
|
+
await this.ensureUserData();
|
|
1951
1831
|
return this.orderBuilder.buildOrder(params);
|
|
1952
1832
|
}
|
|
1953
1833
|
/**
|
|
@@ -1968,308 +1848,35 @@ var OrderClient = class {
|
|
|
1968
1848
|
async signOrder(order) {
|
|
1969
1849
|
return await this.orderSigner.signOrder(order, this.signingConfig);
|
|
1970
1850
|
}
|
|
1971
|
-
};
|
|
1972
|
-
|
|
1973
|
-
// src/portfolio/fetcher.ts
|
|
1974
|
-
var PortfolioFetcher = class {
|
|
1975
|
-
/**
|
|
1976
|
-
* Creates a new portfolio fetcher instance.
|
|
1977
|
-
*
|
|
1978
|
-
* @param httpClient - Authenticated HTTP client for API requests
|
|
1979
|
-
* @param logger - Optional logger for debugging (default: no logging)
|
|
1980
|
-
*
|
|
1981
|
-
* @example
|
|
1982
|
-
* ```typescript
|
|
1983
|
-
* // Create authenticated client
|
|
1984
|
-
* const httpClient = new HttpClient({ baseURL: API_URL });
|
|
1985
|
-
* await authenticator.authenticate({ client: 'eoa' });
|
|
1986
|
-
*
|
|
1987
|
-
* // Create portfolio fetcher
|
|
1988
|
-
* const portfolioFetcher = new PortfolioFetcher(httpClient);
|
|
1989
|
-
* ```
|
|
1990
|
-
*/
|
|
1991
|
-
constructor(httpClient, logger) {
|
|
1992
|
-
this.httpClient = httpClient;
|
|
1993
|
-
this.logger = logger || new NoOpLogger();
|
|
1994
|
-
}
|
|
1995
1851
|
/**
|
|
1996
|
-
* Gets
|
|
1997
|
-
*
|
|
1998
|
-
* @returns Promise resolving to portfolio positions response with CLOB and AMM positions
|
|
1999
|
-
* @throws Error if API request fails or user is not authenticated
|
|
2000
|
-
*
|
|
2001
|
-
* @example
|
|
2002
|
-
* ```typescript
|
|
2003
|
-
* const response = await portfolioFetcher.getPositions();
|
|
2004
|
-
* console.log(`CLOB positions: ${response.clob.length}`);
|
|
2005
|
-
* console.log(`AMM positions: ${response.amm.length}`);
|
|
2006
|
-
* console.log(`Total points: ${response.accumulativePoints}`);
|
|
2007
|
-
* ```
|
|
2008
|
-
*/
|
|
2009
|
-
async getPositions() {
|
|
2010
|
-
this.logger.debug("Fetching user positions");
|
|
2011
|
-
try {
|
|
2012
|
-
const response = await this.httpClient.get(
|
|
2013
|
-
"/portfolio/positions"
|
|
2014
|
-
);
|
|
2015
|
-
this.logger.info("Positions fetched successfully", {
|
|
2016
|
-
clobCount: response.clob?.length || 0,
|
|
2017
|
-
ammCount: response.amm?.length || 0
|
|
2018
|
-
});
|
|
2019
|
-
return response;
|
|
2020
|
-
} catch (error) {
|
|
2021
|
-
this.logger.error("Failed to fetch positions", error);
|
|
2022
|
-
throw error;
|
|
2023
|
-
}
|
|
2024
|
-
}
|
|
2025
|
-
/**
|
|
2026
|
-
* Gets CLOB positions only.
|
|
2027
|
-
*
|
|
2028
|
-
* @returns Promise resolving to array of CLOB positions
|
|
2029
|
-
* @throws Error if API request fails
|
|
2030
|
-
*
|
|
2031
|
-
* @example
|
|
2032
|
-
* ```typescript
|
|
2033
|
-
* const clobPositions = await portfolioFetcher.getCLOBPositions();
|
|
2034
|
-
* clobPositions.forEach(pos => {
|
|
2035
|
-
* console.log(`${pos.market.title}: YES ${pos.positions.yes.unrealizedPnl} P&L`);
|
|
2036
|
-
* });
|
|
2037
|
-
* ```
|
|
2038
|
-
*/
|
|
2039
|
-
async getCLOBPositions() {
|
|
2040
|
-
const response = await this.getPositions();
|
|
2041
|
-
return response.clob || [];
|
|
2042
|
-
}
|
|
2043
|
-
/**
|
|
2044
|
-
* Gets AMM positions only.
|
|
2045
|
-
*
|
|
2046
|
-
* @returns Promise resolving to array of AMM positions
|
|
2047
|
-
* @throws Error if API request fails
|
|
2048
|
-
*
|
|
2049
|
-
* @example
|
|
2050
|
-
* ```typescript
|
|
2051
|
-
* const ammPositions = await portfolioFetcher.getAMMPositions();
|
|
2052
|
-
* ammPositions.forEach(pos => {
|
|
2053
|
-
* console.log(`${pos.market.title}: ${pos.unrealizedPnl} P&L`);
|
|
2054
|
-
* });
|
|
2055
|
-
* ```
|
|
2056
|
-
*/
|
|
2057
|
-
async getAMMPositions() {
|
|
2058
|
-
const response = await this.getPositions();
|
|
2059
|
-
return response.amm || [];
|
|
2060
|
-
}
|
|
2061
|
-
/**
|
|
2062
|
-
* Flattens positions into a unified format for easier consumption.
|
|
2063
|
-
*
|
|
2064
|
-
* @remarks
|
|
2065
|
-
* Converts CLOB positions (which have YES/NO sides) and AMM positions
|
|
2066
|
-
* into a unified Position array. Only includes positions with non-zero values.
|
|
2067
|
-
*
|
|
2068
|
-
* @returns Promise resolving to array of flattened positions
|
|
2069
|
-
* @throws Error if API request fails
|
|
2070
|
-
*
|
|
2071
|
-
* @example
|
|
2072
|
-
* ```typescript
|
|
2073
|
-
* const positions = await portfolioFetcher.getFlattenedPositions();
|
|
2074
|
-
* positions.forEach(pos => {
|
|
2075
|
-
* const pnlPercent = (pos.unrealizedPnl / pos.costBasis) * 100;
|
|
2076
|
-
* console.log(`${pos.market.title} (${pos.side}): ${pnlPercent.toFixed(2)}% P&L`);
|
|
2077
|
-
* });
|
|
2078
|
-
* ```
|
|
2079
|
-
*/
|
|
2080
|
-
async getFlattenedPositions() {
|
|
2081
|
-
const response = await this.getPositions();
|
|
2082
|
-
const positions = [];
|
|
2083
|
-
for (const clobPos of response.clob || []) {
|
|
2084
|
-
const yesCost = parseFloat(clobPos.positions.yes.cost);
|
|
2085
|
-
const yesValue = parseFloat(clobPos.positions.yes.marketValue);
|
|
2086
|
-
if (yesCost > 0 || yesValue > 0) {
|
|
2087
|
-
positions.push({
|
|
2088
|
-
type: "CLOB",
|
|
2089
|
-
market: clobPos.market,
|
|
2090
|
-
side: "YES",
|
|
2091
|
-
costBasis: yesCost,
|
|
2092
|
-
marketValue: yesValue,
|
|
2093
|
-
unrealizedPnl: parseFloat(clobPos.positions.yes.unrealizedPnl),
|
|
2094
|
-
realizedPnl: parseFloat(clobPos.positions.yes.realisedPnl),
|
|
2095
|
-
currentPrice: clobPos.latestTrade?.latestYesPrice ?? 0,
|
|
2096
|
-
avgPrice: yesCost > 0 ? parseFloat(clobPos.positions.yes.fillPrice) / 1e6 : 0,
|
|
2097
|
-
tokenBalance: parseFloat(clobPos.tokensBalance.yes)
|
|
2098
|
-
});
|
|
2099
|
-
}
|
|
2100
|
-
const noCost = parseFloat(clobPos.positions.no.cost);
|
|
2101
|
-
const noValue = parseFloat(clobPos.positions.no.marketValue);
|
|
2102
|
-
if (noCost > 0 || noValue > 0) {
|
|
2103
|
-
positions.push({
|
|
2104
|
-
type: "CLOB",
|
|
2105
|
-
market: clobPos.market,
|
|
2106
|
-
side: "NO",
|
|
2107
|
-
costBasis: noCost,
|
|
2108
|
-
marketValue: noValue,
|
|
2109
|
-
unrealizedPnl: parseFloat(clobPos.positions.no.unrealizedPnl),
|
|
2110
|
-
realizedPnl: parseFloat(clobPos.positions.no.realisedPnl),
|
|
2111
|
-
currentPrice: clobPos.latestTrade?.latestNoPrice ?? 0,
|
|
2112
|
-
avgPrice: noCost > 0 ? parseFloat(clobPos.positions.no.fillPrice) / 1e6 : 0,
|
|
2113
|
-
tokenBalance: parseFloat(clobPos.tokensBalance.no)
|
|
2114
|
-
});
|
|
2115
|
-
}
|
|
2116
|
-
}
|
|
2117
|
-
for (const ammPos of response.amm || []) {
|
|
2118
|
-
const cost = parseFloat(ammPos.totalBuysCost);
|
|
2119
|
-
const value = parseFloat(ammPos.collateralAmount);
|
|
2120
|
-
if (cost > 0 || value > 0) {
|
|
2121
|
-
positions.push({
|
|
2122
|
-
type: "AMM",
|
|
2123
|
-
market: ammPos.market,
|
|
2124
|
-
side: ammPos.outcomeIndex === 0 ? "YES" : "NO",
|
|
2125
|
-
costBasis: cost,
|
|
2126
|
-
marketValue: value,
|
|
2127
|
-
unrealizedPnl: parseFloat(ammPos.unrealizedPnl),
|
|
2128
|
-
realizedPnl: parseFloat(ammPos.realizedPnl),
|
|
2129
|
-
currentPrice: ammPos.latestTrade ? parseFloat(ammPos.latestTrade.outcomeTokenPrice) : 0,
|
|
2130
|
-
avgPrice: parseFloat(ammPos.averageFillPrice),
|
|
2131
|
-
tokenBalance: parseFloat(ammPos.outcomeTokenAmount)
|
|
2132
|
-
});
|
|
2133
|
-
}
|
|
2134
|
-
}
|
|
2135
|
-
this.logger.debug("Flattened positions", { count: positions.length });
|
|
2136
|
-
return positions;
|
|
2137
|
-
}
|
|
2138
|
-
/**
|
|
2139
|
-
* Calculates portfolio summary statistics from raw API response.
|
|
1852
|
+
* Gets the wallet address.
|
|
2140
1853
|
*
|
|
2141
|
-
* @
|
|
2142
|
-
* @returns Portfolio summary with totals and statistics
|
|
1854
|
+
* @returns Ethereum address of the wallet
|
|
2143
1855
|
*
|
|
2144
1856
|
* @example
|
|
2145
1857
|
* ```typescript
|
|
2146
|
-
* const
|
|
2147
|
-
*
|
|
2148
|
-
*
|
|
2149
|
-
* console.log(`Total Portfolio Value: $${(summary.totalValue / 1e6).toFixed(2)}`);
|
|
2150
|
-
* console.log(`Total P&L: ${summary.totalUnrealizedPnlPercent.toFixed(2)}%`);
|
|
2151
|
-
* console.log(`CLOB Positions: ${summary.breakdown.clob.positions}`);
|
|
2152
|
-
* console.log(`AMM Positions: ${summary.breakdown.amm.positions}`);
|
|
1858
|
+
* const address = orderClient.walletAddress;
|
|
1859
|
+
* console.log(`Wallet: ${address}`);
|
|
2153
1860
|
* ```
|
|
2154
1861
|
*/
|
|
2155
|
-
|
|
2156
|
-
this.
|
|
2157
|
-
clobCount: response.clob?.length || 0,
|
|
2158
|
-
ammCount: response.amm?.length || 0
|
|
2159
|
-
});
|
|
2160
|
-
let totalValue = 0;
|
|
2161
|
-
let totalCostBasis = 0;
|
|
2162
|
-
let totalUnrealizedPnl = 0;
|
|
2163
|
-
let totalRealizedPnl = 0;
|
|
2164
|
-
let clobPositions = 0;
|
|
2165
|
-
let clobValue = 0;
|
|
2166
|
-
let clobPnl = 0;
|
|
2167
|
-
let ammPositions = 0;
|
|
2168
|
-
let ammValue = 0;
|
|
2169
|
-
let ammPnl = 0;
|
|
2170
|
-
for (const clobPos of response.clob || []) {
|
|
2171
|
-
const yesCost = parseFloat(clobPos.positions.yes.cost);
|
|
2172
|
-
const yesValue = parseFloat(clobPos.positions.yes.marketValue);
|
|
2173
|
-
const yesUnrealizedPnl = parseFloat(clobPos.positions.yes.unrealizedPnl);
|
|
2174
|
-
const yesRealizedPnl = parseFloat(clobPos.positions.yes.realisedPnl);
|
|
2175
|
-
if (yesCost > 0 || yesValue > 0) {
|
|
2176
|
-
clobPositions++;
|
|
2177
|
-
totalCostBasis += yesCost;
|
|
2178
|
-
totalValue += yesValue;
|
|
2179
|
-
totalUnrealizedPnl += yesUnrealizedPnl;
|
|
2180
|
-
totalRealizedPnl += yesRealizedPnl;
|
|
2181
|
-
clobValue += yesValue;
|
|
2182
|
-
clobPnl += yesUnrealizedPnl;
|
|
2183
|
-
}
|
|
2184
|
-
const noCost = parseFloat(clobPos.positions.no.cost);
|
|
2185
|
-
const noValue = parseFloat(clobPos.positions.no.marketValue);
|
|
2186
|
-
const noUnrealizedPnl = parseFloat(clobPos.positions.no.unrealizedPnl);
|
|
2187
|
-
const noRealizedPnl = parseFloat(clobPos.positions.no.realisedPnl);
|
|
2188
|
-
if (noCost > 0 || noValue > 0) {
|
|
2189
|
-
clobPositions++;
|
|
2190
|
-
totalCostBasis += noCost;
|
|
2191
|
-
totalValue += noValue;
|
|
2192
|
-
totalUnrealizedPnl += noUnrealizedPnl;
|
|
2193
|
-
totalRealizedPnl += noRealizedPnl;
|
|
2194
|
-
clobValue += noValue;
|
|
2195
|
-
clobPnl += noUnrealizedPnl;
|
|
2196
|
-
}
|
|
2197
|
-
}
|
|
2198
|
-
for (const ammPos of response.amm || []) {
|
|
2199
|
-
const cost = parseFloat(ammPos.totalBuysCost);
|
|
2200
|
-
const value = parseFloat(ammPos.collateralAmount);
|
|
2201
|
-
const unrealizedPnl = parseFloat(ammPos.unrealizedPnl);
|
|
2202
|
-
const realizedPnl = parseFloat(ammPos.realizedPnl);
|
|
2203
|
-
if (cost > 0 || value > 0) {
|
|
2204
|
-
ammPositions++;
|
|
2205
|
-
totalCostBasis += cost;
|
|
2206
|
-
totalValue += value;
|
|
2207
|
-
totalUnrealizedPnl += unrealizedPnl;
|
|
2208
|
-
totalRealizedPnl += realizedPnl;
|
|
2209
|
-
ammValue += value;
|
|
2210
|
-
ammPnl += unrealizedPnl;
|
|
2211
|
-
}
|
|
2212
|
-
}
|
|
2213
|
-
const totalUnrealizedPnlPercent = totalCostBasis > 0 ? totalUnrealizedPnl / totalCostBasis * 100 : 0;
|
|
2214
|
-
const uniqueMarkets = /* @__PURE__ */ new Set();
|
|
2215
|
-
for (const pos of response.clob || []) {
|
|
2216
|
-
uniqueMarkets.add(pos.market.id);
|
|
2217
|
-
}
|
|
2218
|
-
for (const pos of response.amm || []) {
|
|
2219
|
-
uniqueMarkets.add(pos.market.id);
|
|
2220
|
-
}
|
|
2221
|
-
const summary = {
|
|
2222
|
-
totalValue,
|
|
2223
|
-
totalCostBasis,
|
|
2224
|
-
totalUnrealizedPnl,
|
|
2225
|
-
totalRealizedPnl,
|
|
2226
|
-
totalUnrealizedPnlPercent,
|
|
2227
|
-
positionCount: clobPositions + ammPositions,
|
|
2228
|
-
marketCount: uniqueMarkets.size,
|
|
2229
|
-
breakdown: {
|
|
2230
|
-
clob: {
|
|
2231
|
-
positions: clobPositions,
|
|
2232
|
-
value: clobValue,
|
|
2233
|
-
pnl: clobPnl
|
|
2234
|
-
},
|
|
2235
|
-
amm: {
|
|
2236
|
-
positions: ammPositions,
|
|
2237
|
-
value: ammValue,
|
|
2238
|
-
pnl: ammPnl
|
|
2239
|
-
}
|
|
2240
|
-
}
|
|
2241
|
-
};
|
|
2242
|
-
this.logger.debug("Portfolio summary calculated", summary);
|
|
2243
|
-
return summary;
|
|
1862
|
+
get walletAddress() {
|
|
1863
|
+
return this.wallet.address;
|
|
2244
1864
|
}
|
|
2245
1865
|
/**
|
|
2246
|
-
* Gets
|
|
1866
|
+
* Gets the owner ID (user ID from profile).
|
|
2247
1867
|
*
|
|
2248
|
-
* @returns
|
|
2249
|
-
* @throws Error if API request fails or user is not authenticated
|
|
1868
|
+
* @returns Owner ID from user profile, or undefined if not yet loaded
|
|
2250
1869
|
*
|
|
2251
1870
|
* @example
|
|
2252
1871
|
* ```typescript
|
|
2253
|
-
* const
|
|
2254
|
-
*
|
|
2255
|
-
*
|
|
2256
|
-
*
|
|
2257
|
-
* console.log(` Total P&L: $${(summary.totalUnrealizedPnl / 1e6).toFixed(2)}`);
|
|
2258
|
-
* console.log(` P&L %: ${summary.totalUnrealizedPnlPercent.toFixed(2)}%`);
|
|
2259
|
-
* console.log(`\nCLOB Positions: ${response.clob.length}`);
|
|
2260
|
-
* console.log(`AMM Positions: ${response.amm.length}`);
|
|
1872
|
+
* const ownerId = orderClient.ownerId;
|
|
1873
|
+
* if (ownerId) {
|
|
1874
|
+
* console.log(`Owner ID: ${ownerId}`);
|
|
1875
|
+
* }
|
|
2261
1876
|
* ```
|
|
2262
1877
|
*/
|
|
2263
|
-
|
|
2264
|
-
this.
|
|
2265
|
-
const response = await this.getPositions();
|
|
2266
|
-
const summary = this.calculateSummary(response);
|
|
2267
|
-
this.logger.info("Portfolio fetched with summary", {
|
|
2268
|
-
positionCount: summary.positionCount,
|
|
2269
|
-
totalValueUSDC: summary.totalValue / 1e6,
|
|
2270
|
-
pnlPercent: summary.totalUnrealizedPnlPercent
|
|
2271
|
-
});
|
|
2272
|
-
return { response, summary };
|
|
1878
|
+
get ownerId() {
|
|
1879
|
+
return this.cachedUserData?.userId;
|
|
2273
1880
|
}
|
|
2274
1881
|
};
|
|
2275
1882
|
|
|
@@ -2290,7 +1897,7 @@ var WebSocketClient = class {
|
|
|
2290
1897
|
this.pendingListeners = [];
|
|
2291
1898
|
this.config = {
|
|
2292
1899
|
url: config.url || DEFAULT_WS_URL,
|
|
2293
|
-
|
|
1900
|
+
apiKey: config.apiKey || process.env.LIMITLESS_API_KEY || "",
|
|
2294
1901
|
autoReconnect: config.autoReconnect ?? true,
|
|
2295
1902
|
reconnectDelay: config.reconnectDelay || 1e3,
|
|
2296
1903
|
maxReconnectAttempts: config.maxReconnectAttempts || Infinity,
|
|
@@ -2315,18 +1922,29 @@ var WebSocketClient = class {
|
|
|
2315
1922
|
return this.state === "connected" /* CONNECTED */ && this.socket?.connected === true;
|
|
2316
1923
|
}
|
|
2317
1924
|
/**
|
|
2318
|
-
* Sets the
|
|
1925
|
+
* Sets the API key for authentication.
|
|
2319
1926
|
*
|
|
2320
|
-
* @param
|
|
1927
|
+
* @param apiKey - API key value
|
|
1928
|
+
*
|
|
1929
|
+
* @remarks
|
|
1930
|
+
* API key is required for authenticated subscriptions (positions, transactions).
|
|
1931
|
+
* If already connected, this will trigger a reconnection with the new API key.
|
|
2321
1932
|
*/
|
|
2322
|
-
|
|
2323
|
-
this.config.
|
|
1933
|
+
setApiKey(apiKey) {
|
|
1934
|
+
this.config.apiKey = apiKey;
|
|
2324
1935
|
if (this.socket?.connected) {
|
|
2325
|
-
this.logger.info("
|
|
2326
|
-
this.
|
|
2327
|
-
this.connect();
|
|
1936
|
+
this.logger.info("API key updated, reconnecting...");
|
|
1937
|
+
this.reconnectWithNewAuth();
|
|
2328
1938
|
}
|
|
2329
1939
|
}
|
|
1940
|
+
/**
|
|
1941
|
+
* Reconnects with new authentication credentials.
|
|
1942
|
+
* @internal
|
|
1943
|
+
*/
|
|
1944
|
+
async reconnectWithNewAuth() {
|
|
1945
|
+
await this.disconnect();
|
|
1946
|
+
await this.connect();
|
|
1947
|
+
}
|
|
2330
1948
|
/**
|
|
2331
1949
|
* Connects to the WebSocket server.
|
|
2332
1950
|
*
|
|
@@ -2340,8 +1958,8 @@ var WebSocketClient = class {
|
|
|
2340
1958
|
* ```
|
|
2341
1959
|
*/
|
|
2342
1960
|
async connect() {
|
|
2343
|
-
if (this.socket?.connected) {
|
|
2344
|
-
this.logger.info("Already connected");
|
|
1961
|
+
if (this.socket?.connected || this.state === "connecting" /* CONNECTING */) {
|
|
1962
|
+
this.logger.info("Already connected or connecting");
|
|
2345
1963
|
return;
|
|
2346
1964
|
}
|
|
2347
1965
|
this.logger.info("Connecting to WebSocket", { url: this.config.url });
|
|
@@ -2350,18 +1968,26 @@ var WebSocketClient = class {
|
|
|
2350
1968
|
const timeout = setTimeout(() => {
|
|
2351
1969
|
reject(new Error(`Connection timeout after ${this.config.timeout}ms`));
|
|
2352
1970
|
}, this.config.timeout);
|
|
2353
|
-
const wsUrl = this.config.url
|
|
2354
|
-
|
|
1971
|
+
const wsUrl = this.config.url;
|
|
1972
|
+
const socketOptions = {
|
|
2355
1973
|
transports: ["websocket"],
|
|
2356
1974
|
// Use WebSocket transport only
|
|
2357
|
-
extraHeaders: {
|
|
2358
|
-
cookie: `limitless_session=${this.config.sessionCookie}`
|
|
2359
|
-
},
|
|
2360
1975
|
reconnection: this.config.autoReconnect,
|
|
2361
1976
|
reconnectionDelay: this.config.reconnectDelay,
|
|
2362
|
-
|
|
1977
|
+
reconnectionDelayMax: Math.min(this.config.reconnectDelay * 32, 6e4),
|
|
1978
|
+
// Max 60s
|
|
1979
|
+
reconnectionAttempts: this.config.maxReconnectAttempts === Infinity ? 0 : this.config.maxReconnectAttempts,
|
|
1980
|
+
// 0 = infinite
|
|
1981
|
+
randomizationFactor: 0.2,
|
|
1982
|
+
// Add jitter to prevent thundering herd
|
|
2363
1983
|
timeout: this.config.timeout
|
|
2364
|
-
}
|
|
1984
|
+
};
|
|
1985
|
+
if (this.config.apiKey) {
|
|
1986
|
+
socketOptions.extraHeaders = {
|
|
1987
|
+
"X-API-Key": this.config.apiKey
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
this.socket = (0, import_socket.io)(wsUrl + "/markets", socketOptions);
|
|
2365
1991
|
this.attachPendingListeners();
|
|
2366
1992
|
this.setupEventHandlers();
|
|
2367
1993
|
this.socket.once("connect", () => {
|
|
@@ -2383,12 +2009,14 @@ var WebSocketClient = class {
|
|
|
2383
2009
|
/**
|
|
2384
2010
|
* Disconnects from the WebSocket server.
|
|
2385
2011
|
*
|
|
2012
|
+
* @returns Promise that resolves when disconnected
|
|
2013
|
+
*
|
|
2386
2014
|
* @example
|
|
2387
2015
|
* ```typescript
|
|
2388
|
-
* wsClient.disconnect();
|
|
2016
|
+
* await wsClient.disconnect();
|
|
2389
2017
|
* ```
|
|
2390
2018
|
*/
|
|
2391
|
-
disconnect() {
|
|
2019
|
+
async disconnect() {
|
|
2392
2020
|
if (!this.socket) {
|
|
2393
2021
|
return;
|
|
2394
2022
|
}
|
|
@@ -2403,13 +2031,13 @@ var WebSocketClient = class {
|
|
|
2403
2031
|
*
|
|
2404
2032
|
* @param channel - Channel to subscribe to
|
|
2405
2033
|
* @param options - Subscription options
|
|
2406
|
-
* @returns Promise that resolves
|
|
2034
|
+
* @returns Promise that resolves immediately (kept async for API compatibility)
|
|
2407
2035
|
* @throws Error if not connected
|
|
2408
2036
|
*
|
|
2409
2037
|
* @example
|
|
2410
2038
|
* ```typescript
|
|
2411
2039
|
* // Subscribe to orderbook for a specific market
|
|
2412
|
-
* await wsClient.subscribe('orderbook', {
|
|
2040
|
+
* await wsClient.subscribe('orderbook', { marketSlugs: ['market-123'] });
|
|
2413
2041
|
*
|
|
2414
2042
|
* // Subscribe to all trades
|
|
2415
2043
|
* await wsClient.subscribe('trades');
|
|
@@ -2422,21 +2050,20 @@ var WebSocketClient = class {
|
|
|
2422
2050
|
if (!this.isConnected()) {
|
|
2423
2051
|
throw new Error("WebSocket not connected. Call connect() first.");
|
|
2424
2052
|
}
|
|
2053
|
+
const authenticatedChannels = [
|
|
2054
|
+
"subscribe_positions",
|
|
2055
|
+
"subscribe_transactions"
|
|
2056
|
+
];
|
|
2057
|
+
if (authenticatedChannels.includes(channel) && !this.config.apiKey) {
|
|
2058
|
+
throw new Error(
|
|
2059
|
+
`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`
|
|
2060
|
+
);
|
|
2061
|
+
}
|
|
2425
2062
|
const subscriptionKey = this.getSubscriptionKey(channel, options);
|
|
2426
2063
|
this.subscriptions.set(subscriptionKey, options);
|
|
2427
2064
|
this.logger.info("Subscribing to channel", { channel, options });
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
if (response?.error) {
|
|
2431
|
-
this.logger.error("Subscription failed", response.error);
|
|
2432
|
-
this.subscriptions.delete(subscriptionKey);
|
|
2433
|
-
reject(new Error(response.error));
|
|
2434
|
-
} else {
|
|
2435
|
-
this.logger.info("Subscribed successfully", { channel, options });
|
|
2436
|
-
resolve();
|
|
2437
|
-
}
|
|
2438
|
-
});
|
|
2439
|
-
});
|
|
2065
|
+
this.socket.emit(channel, options);
|
|
2066
|
+
this.logger.info("Subscription request sent", { channel, options });
|
|
2440
2067
|
}
|
|
2441
2068
|
/**
|
|
2442
2069
|
* Unsubscribes from a channel.
|
|
@@ -2444,10 +2071,11 @@ var WebSocketClient = class {
|
|
|
2444
2071
|
* @param channel - Channel to unsubscribe from
|
|
2445
2072
|
* @param options - Subscription options (must match subscribe call)
|
|
2446
2073
|
* @returns Promise that resolves when unsubscribed
|
|
2074
|
+
* @throws Error if not connected or unsubscribe fails
|
|
2447
2075
|
*
|
|
2448
2076
|
* @example
|
|
2449
2077
|
* ```typescript
|
|
2450
|
-
* await wsClient.unsubscribe('orderbook', {
|
|
2078
|
+
* await wsClient.unsubscribe('orderbook', { marketSlugs: ['market-123'] });
|
|
2451
2079
|
* ```
|
|
2452
2080
|
*/
|
|
2453
2081
|
async unsubscribe(channel, options = {}) {
|
|
@@ -2457,17 +2085,19 @@ var WebSocketClient = class {
|
|
|
2457
2085
|
const subscriptionKey = this.getSubscriptionKey(channel, options);
|
|
2458
2086
|
this.subscriptions.delete(subscriptionKey);
|
|
2459
2087
|
this.logger.info("Unsubscribing from channel", { channel, options });
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2088
|
+
try {
|
|
2089
|
+
const unsubscribeData = { channel, ...options };
|
|
2090
|
+
const response = await this.socket.timeout(5e3).emitWithAck("unsubscribe", unsubscribeData);
|
|
2091
|
+
if (response && typeof response === "object" && "error" in response) {
|
|
2092
|
+
const errorMsg = response.error;
|
|
2093
|
+
this.logger.error("Unsubscribe failed", new Error(errorMsg), { error: errorMsg });
|
|
2094
|
+
throw new Error(`Unsubscribe failed: ${errorMsg}`);
|
|
2095
|
+
}
|
|
2096
|
+
this.logger.info("Unsubscribed successfully", { channel, options });
|
|
2097
|
+
} catch (error) {
|
|
2098
|
+
this.logger.error("Unsubscribe error", error, { channel });
|
|
2099
|
+
throw error;
|
|
2100
|
+
}
|
|
2471
2101
|
}
|
|
2472
2102
|
/**
|
|
2473
2103
|
* Registers an event listener.
|
|
@@ -2510,14 +2140,27 @@ var WebSocketClient = class {
|
|
|
2510
2140
|
* Removes an event listener.
|
|
2511
2141
|
*
|
|
2512
2142
|
* @param event - Event name
|
|
2513
|
-
* @param handler - Event handler to remove
|
|
2143
|
+
* @param handler - Event handler to remove (if undefined, removes all handlers for event)
|
|
2514
2144
|
* @returns This client for chaining
|
|
2145
|
+
*
|
|
2146
|
+
* @example
|
|
2147
|
+
* ```typescript
|
|
2148
|
+
* // Remove specific handler
|
|
2149
|
+
* wsClient.off('orderbookUpdate', myHandler);
|
|
2150
|
+
*
|
|
2151
|
+
* // Remove all handlers for event
|
|
2152
|
+
* wsClient.off('orderbookUpdate');
|
|
2153
|
+
* ```
|
|
2515
2154
|
*/
|
|
2516
2155
|
off(event, handler) {
|
|
2517
2156
|
if (!this.socket) {
|
|
2518
2157
|
return this;
|
|
2519
2158
|
}
|
|
2520
|
-
|
|
2159
|
+
if (handler === void 0) {
|
|
2160
|
+
this.socket.removeAllListeners(event);
|
|
2161
|
+
} else {
|
|
2162
|
+
this.socket.off(event, handler);
|
|
2163
|
+
}
|
|
2521
2164
|
return this;
|
|
2522
2165
|
}
|
|
2523
2166
|
/**
|
|
@@ -2610,8 +2253,7 @@ var WebSocketClient = class {
|
|
|
2610
2253
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2611
2254
|
0 && (module.exports = {
|
|
2612
2255
|
APIError,
|
|
2613
|
-
|
|
2614
|
-
Authenticator,
|
|
2256
|
+
AuthenticationError,
|
|
2615
2257
|
BASE_SEPOLIA_CHAIN_ID,
|
|
2616
2258
|
CONTRACT_ADDRESSES,
|
|
2617
2259
|
ConsoleLogger,
|
|
@@ -2619,14 +2261,16 @@ var WebSocketClient = class {
|
|
|
2619
2261
|
DEFAULT_CHAIN_ID,
|
|
2620
2262
|
DEFAULT_WS_URL,
|
|
2621
2263
|
HttpClient,
|
|
2264
|
+
Market,
|
|
2622
2265
|
MarketFetcher,
|
|
2623
|
-
MessageSigner,
|
|
2624
2266
|
NoOpLogger,
|
|
2625
2267
|
OrderBuilder,
|
|
2626
2268
|
OrderClient,
|
|
2627
2269
|
OrderSigner,
|
|
2628
2270
|
OrderType,
|
|
2271
|
+
OrderValidationError,
|
|
2629
2272
|
PortfolioFetcher,
|
|
2273
|
+
RateLimitError,
|
|
2630
2274
|
RetryConfig,
|
|
2631
2275
|
RetryableClient,
|
|
2632
2276
|
SIGNING_MESSAGE_TEMPLATE,
|