@limitless-exchange/sdk 1.0.3 → 1.0.4

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.js CHANGED
@@ -31,13 +31,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  APIError: () => APIError,
34
+ ApiTokenService: () => ApiTokenService,
34
35
  AuthenticationError: () => AuthenticationError,
35
36
  BASE_SEPOLIA_CHAIN_ID: () => BASE_SEPOLIA_CHAIN_ID,
36
37
  CONTRACT_ADDRESSES: () => CONTRACT_ADDRESSES,
38
+ Client: () => Client,
37
39
  ConsoleLogger: () => ConsoleLogger,
38
40
  DEFAULT_API_URL: () => DEFAULT_API_URL,
39
41
  DEFAULT_CHAIN_ID: () => DEFAULT_CHAIN_ID,
40
42
  DEFAULT_WS_URL: () => DEFAULT_WS_URL,
43
+ DelegatedOrderService: () => DelegatedOrderService,
41
44
  HttpClient: () => HttpClient,
42
45
  Market: () => Market,
43
46
  MarketFetcher: () => MarketFetcher,
@@ -48,19 +51,27 @@ __export(index_exports, {
48
51
  OrderSigner: () => OrderSigner,
49
52
  OrderType: () => OrderType,
50
53
  OrderValidationError: () => OrderValidationError,
54
+ PartnerAccountService: () => PartnerAccountService,
51
55
  PortfolioFetcher: () => PortfolioFetcher,
52
56
  RateLimitError: () => RateLimitError,
53
57
  RetryConfig: () => RetryConfig,
54
58
  RetryableClient: () => RetryableClient,
55
59
  SIGNING_MESSAGE_TEMPLATE: () => SIGNING_MESSAGE_TEMPLATE,
60
+ ScopeAccountCreation: () => ScopeAccountCreation,
61
+ ScopeDelegatedSigning: () => ScopeDelegatedSigning,
62
+ ScopeTrading: () => ScopeTrading,
56
63
  Side: () => Side,
57
64
  SignatureType: () => SignatureType,
58
65
  ValidationError: () => ValidationError,
59
66
  WebSocketClient: () => WebSocketClient,
60
67
  WebSocketState: () => WebSocketState,
61
68
  ZERO_ADDRESS: () => ZERO_ADDRESS,
69
+ buildHMACMessage: () => buildHMACMessage,
70
+ computeHMACSignature: () => computeHMACSignature,
62
71
  getContractAddress: () => getContractAddress,
63
72
  retryOnErrors: () => retryOnErrors,
73
+ toFiniteInteger: () => toFiniteInteger,
74
+ toFiniteNumber: () => toFiniteNumber,
64
75
  validateOrderArgs: () => validateOrderArgs,
65
76
  validateSignedOrder: () => validateSignedOrder,
66
77
  validateUnsignedOrder: () => validateUnsignedOrder,
@@ -68,6 +79,11 @@ __export(index_exports, {
68
79
  });
69
80
  module.exports = __toCommonJS(index_exports);
70
81
 
82
+ // src/types/api-tokens.ts
83
+ var ScopeTrading = "trading";
84
+ var ScopeAccountCreation = "account_creation";
85
+ var ScopeDelegatedSigning = "delegated_signing";
86
+
71
87
  // src/types/logger.ts
72
88
  var NoOpLogger = class {
73
89
  debug() {
@@ -225,6 +241,40 @@ function getContractAddress(contractType, chainId = DEFAULT_CHAIN_ID) {
225
241
  return address;
226
242
  }
227
243
 
244
+ // src/utils/sdk-tracking.ts
245
+ var SDK_ID = "lmts-sdk-ts";
246
+ function isNodeRuntime() {
247
+ return typeof process !== "undefined" && !!process.versions?.node;
248
+ }
249
+ function resolveSdkVersion() {
250
+ if ("1.0.4") {
251
+ return "1.0.4";
252
+ }
253
+ return "0.0.0";
254
+ }
255
+ function resolveRuntimeToken() {
256
+ if (isNodeRuntime()) {
257
+ return `node/${process.versions.node}`;
258
+ }
259
+ return "runtime/unknown";
260
+ }
261
+ function buildSdkTrackingHeaders() {
262
+ const sdkVersion = resolveSdkVersion();
263
+ const headers = {
264
+ "x-sdk-version": `${SDK_ID}/${sdkVersion}`
265
+ };
266
+ if (isNodeRuntime()) {
267
+ headers["user-agent"] = `${SDK_ID}/${sdkVersion} (${resolveRuntimeToken()})`;
268
+ }
269
+ return headers;
270
+ }
271
+ function buildWebSocketTrackingHeaders() {
272
+ if (!isNodeRuntime()) {
273
+ return {};
274
+ }
275
+ return buildSdkTrackingHeaders();
276
+ }
277
+
228
278
  // src/api/errors.ts
229
279
  var APIError = class _APIError extends Error {
230
280
  /**
@@ -297,6 +347,19 @@ var ValidationError = class _ValidationError extends APIError {
297
347
  }
298
348
  };
299
349
 
350
+ // src/api/hmac.ts
351
+ var import_crypto = require("crypto");
352
+ function buildHMACMessage(timestamp, method, path, body) {
353
+ return `${timestamp}
354
+ ${method.toUpperCase()}
355
+ ${path}
356
+ ${body}`;
357
+ }
358
+ function computeHMACSignature(secret, timestamp, method, path, body) {
359
+ const key = Buffer.from(secret, "base64");
360
+ return (0, import_crypto.createHmac)("sha256", key).update(buildHMACMessage(timestamp, method, path, body)).digest("base64");
361
+ }
362
+
300
363
  // src/api/http.ts
301
364
  var HttpClient = class {
302
365
  /**
@@ -306,10 +369,14 @@ var HttpClient = class {
306
369
  */
307
370
  constructor(config = {}) {
308
371
  this.apiKey = config.apiKey || process.env.LIMITLESS_API_KEY;
372
+ this.hmacCredentials = config.hmacCredentials ? {
373
+ tokenId: config.hmacCredentials.tokenId,
374
+ secret: config.hmacCredentials.secret
375
+ } : void 0;
309
376
  this.logger = config.logger || new NoOpLogger();
310
- if (!this.apiKey) {
377
+ if (!this.apiKey && !this.hmacCredentials) {
311
378
  this.logger.warn(
312
- "API key not set. Authenticated endpoints will fail. Set LIMITLESS_API_KEY environment variable or pass apiKey parameter."
379
+ "Authentication not set. Authenticated endpoints will fail. Set LIMITLESS_API_KEY environment variable, pass apiKey, or configure hmacCredentials."
313
380
  );
314
381
  }
315
382
  const keepAlive = config.keepAlive !== false;
@@ -336,6 +403,7 @@ var HttpClient = class {
336
403
  headers: {
337
404
  "Content-Type": "application/json",
338
405
  Accept: "application/json",
406
+ ...buildSdkTrackingHeaders(),
339
407
  ...config.additionalHeaders
340
408
  }
341
409
  });
@@ -353,21 +421,42 @@ var HttpClient = class {
353
421
  */
354
422
  setupInterceptors() {
355
423
  this.client.interceptors.request.use(
356
- (config) => {
357
- if (this.apiKey) {
358
- config.headers["X-API-Key"] = this.apiKey;
424
+ (rawConfig) => {
425
+ const config = rawConfig;
426
+ const headers = config.headers || (config.headers = {});
427
+ const identityToken = config.identityToken;
428
+ delete headers["X-API-Key"];
429
+ delete headers["lmts-api-key"];
430
+ delete headers["lmts-timestamp"];
431
+ delete headers["lmts-signature"];
432
+ delete headers.identity;
433
+ if (identityToken) {
434
+ headers.identity = `Bearer ${identityToken}`;
435
+ } else if (this.hmacCredentials) {
436
+ const requestPath = this.getRequestPath(config);
437
+ const requestBody = this.getRequestBodyForSignature(config.data);
438
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
439
+ const signature = computeHMACSignature(
440
+ this.hmacCredentials.secret,
441
+ timestamp,
442
+ config.method || "GET",
443
+ requestPath,
444
+ requestBody
445
+ );
446
+ headers["lmts-api-key"] = this.hmacCredentials.tokenId;
447
+ headers["lmts-timestamp"] = timestamp;
448
+ headers["lmts-signature"] = signature;
449
+ } else if (this.apiKey) {
450
+ headers["X-API-Key"] = this.apiKey;
359
451
  }
360
452
  const fullUrl = `${config.baseURL || ""}${config.url || ""}`;
361
453
  const method = config.method?.toUpperCase() || "GET";
362
- const logHeaders = { ...config.headers };
363
- if (logHeaders["X-API-Key"]) {
364
- logHeaders["X-API-Key"] = "***";
365
- }
454
+ const logHeaders = this.maskSensitiveHeaders(headers);
366
455
  this.logger.debug(`\u2192 ${method} ${fullUrl}`, {
367
456
  headers: logHeaders,
368
457
  body: config.data
369
458
  });
370
- return config;
459
+ return rawConfig;
371
460
  },
372
461
  (error) => Promise.reject(error)
373
462
  );
@@ -466,12 +555,86 @@ var HttpClient = class {
466
555
  setApiKey(apiKey) {
467
556
  this.apiKey = apiKey;
468
557
  }
558
+ /**
559
+ * Returns the configured API key, if any.
560
+ */
561
+ getApiKey() {
562
+ return this.apiKey;
563
+ }
469
564
  /**
470
565
  * Clears the API key.
471
566
  */
472
567
  clearApiKey() {
473
568
  this.apiKey = void 0;
474
569
  }
570
+ /**
571
+ * Sets HMAC credentials for scoped API-token authentication.
572
+ */
573
+ setHMACCredentials(credentials) {
574
+ this.hmacCredentials = {
575
+ tokenId: credentials.tokenId,
576
+ secret: credentials.secret
577
+ };
578
+ }
579
+ /**
580
+ * Clears HMAC credentials.
581
+ */
582
+ clearHMACCredentials() {
583
+ this.hmacCredentials = void 0;
584
+ }
585
+ /**
586
+ * Returns a copy of the configured HMAC credentials, if any.
587
+ */
588
+ getHMACCredentials() {
589
+ if (!this.hmacCredentials) {
590
+ return void 0;
591
+ }
592
+ return {
593
+ tokenId: this.hmacCredentials.tokenId,
594
+ secret: this.hmacCredentials.secret
595
+ };
596
+ }
597
+ /**
598
+ * Returns the logger attached to this HTTP client.
599
+ */
600
+ getLogger() {
601
+ return this.logger;
602
+ }
603
+ /**
604
+ * Returns true when cookie/header-based auth is configured on the underlying client.
605
+ * This is primarily used for custom authenticated flows that don't use API keys or HMAC.
606
+ */
607
+ hasConfiguredHeaderAuth() {
608
+ const defaultHeaders = this.client.defaults.headers;
609
+ const candidates = [defaultHeaders?.common, defaultHeaders];
610
+ for (const headers of candidates) {
611
+ if (!headers) {
612
+ continue;
613
+ }
614
+ const authValues = [
615
+ headers.Cookie,
616
+ headers.cookie,
617
+ headers.Authorization,
618
+ headers.authorization,
619
+ headers.identity
620
+ ];
621
+ if (authValues.some((value) => typeof value === "string" && value.trim() !== "")) {
622
+ return true;
623
+ }
624
+ }
625
+ return false;
626
+ }
627
+ /**
628
+ * Ensures the client has some authenticated transport configured.
629
+ */
630
+ requireAuth(operation) {
631
+ if (this.apiKey || this.hmacCredentials || this.hasConfiguredHeaderAuth()) {
632
+ return;
633
+ }
634
+ throw new Error(
635
+ `Authentication is required for ${operation}; pass apiKey, hmacCredentials, cookie/auth headers, or set LIMITLESS_API_KEY.`
636
+ );
637
+ }
475
638
  /**
476
639
  * Performs a GET request.
477
640
  *
@@ -483,6 +646,16 @@ var HttpClient = class {
483
646
  const response = await this.client.get(url, config);
484
647
  return response.data;
485
648
  }
649
+ /**
650
+ * Performs a GET request with identity-token authentication.
651
+ */
652
+ async getWithIdentity(url, identityToken, config) {
653
+ const response = await this.client.get(url, {
654
+ ...config,
655
+ identityToken
656
+ });
657
+ return response.data;
658
+ }
486
659
  /**
487
660
  * Performs a GET request and returns raw response metadata.
488
661
  *
@@ -517,6 +690,36 @@ var HttpClient = class {
517
690
  const response = await this.client.post(url, data, config);
518
691
  return response.data;
519
692
  }
693
+ /**
694
+ * Performs a POST request with identity-token authentication.
695
+ */
696
+ async postWithIdentity(url, identityToken, data, config) {
697
+ const response = await this.client.post(url, data, {
698
+ ...config,
699
+ identityToken
700
+ });
701
+ return response.data;
702
+ }
703
+ /**
704
+ * Performs a POST request with additional per-request headers.
705
+ */
706
+ async postWithHeaders(url, data, headers, config) {
707
+ const response = await this.client.post(url, data, {
708
+ ...config,
709
+ headers: {
710
+ ...config?.headers || {},
711
+ ...headers || {}
712
+ }
713
+ });
714
+ return response.data;
715
+ }
716
+ /**
717
+ * Performs a PATCH request.
718
+ */
719
+ async patch(url, data, config) {
720
+ const response = await this.client.patch(url, data, config);
721
+ return response.data;
722
+ }
520
723
  /**
521
724
  * Performs a DELETE request.
522
725
  *
@@ -539,6 +742,28 @@ var HttpClient = class {
539
742
  const response = await this.client.delete(url, deleteConfig);
540
743
  return response.data;
541
744
  }
745
+ getRequestPath(config) {
746
+ const resolved = new URL(config.url || "", config.baseURL || this.client.defaults.baseURL || DEFAULT_API_URL);
747
+ return `${resolved.pathname}${resolved.search}`;
748
+ }
749
+ getRequestBodyForSignature(data) {
750
+ if (data === void 0 || data === null || data === "") {
751
+ return "";
752
+ }
753
+ if (typeof data === "string") {
754
+ return data;
755
+ }
756
+ return JSON.stringify(data);
757
+ }
758
+ maskSensitiveHeaders(headers) {
759
+ const masked = { ...headers };
760
+ for (const key of ["X-API-Key", "lmts-api-key", "lmts-timestamp", "lmts-signature", "identity"]) {
761
+ if (masked[key] !== void 0) {
762
+ masked[key] = key === "identity" ? "Bearer ***" : "***";
763
+ }
764
+ }
765
+ return masked;
766
+ }
542
767
  };
543
768
 
544
769
  // src/api/retry.ts
@@ -722,6 +947,75 @@ var RetryableClient = class {
722
947
  }
723
948
  };
724
949
 
950
+ // src/api-tokens/service.ts
951
+ var ApiTokenService = class {
952
+ constructor(httpClient, logger) {
953
+ this.httpClient = httpClient;
954
+ this.logger = logger || new NoOpLogger();
955
+ }
956
+ async deriveToken(identityToken, input) {
957
+ if (!identityToken) {
958
+ throw new Error("Identity token is required for deriveToken");
959
+ }
960
+ this.logger.debug("Deriving API token", { scopes: input.scopes, label: input.label });
961
+ return this.httpClient.postWithIdentity("/auth/api-tokens/derive", identityToken, input);
962
+ }
963
+ async listTokens() {
964
+ this.httpClient.requireAuth("listTokens");
965
+ return this.httpClient.get("/auth/api-tokens");
966
+ }
967
+ async getCapabilities(identityToken) {
968
+ if (!identityToken) {
969
+ throw new Error("Identity token is required for getCapabilities");
970
+ }
971
+ return this.httpClient.getWithIdentity("/auth/api-tokens/capabilities", identityToken);
972
+ }
973
+ async revokeToken(tokenId) {
974
+ this.httpClient.requireAuth("revokeToken");
975
+ const response = await this.httpClient.delete(`/auth/api-tokens/${encodeURIComponent(tokenId)}`);
976
+ return response.message;
977
+ }
978
+ };
979
+
980
+ // src/partner-accounts/service.ts
981
+ var _PartnerAccountService = class _PartnerAccountService {
982
+ constructor(httpClient, logger) {
983
+ this.httpClient = httpClient;
984
+ this.logger = logger || new NoOpLogger();
985
+ }
986
+ async createAccount(input, eoaHeaders) {
987
+ this.httpClient.requireAuth("createPartnerAccount");
988
+ const serverWalletMode = input.createServerWallet === true;
989
+ if (!serverWalletMode && !eoaHeaders) {
990
+ throw new Error("EOA headers are required when createServerWallet is not true");
991
+ }
992
+ if (input.displayName && input.displayName.length > _PartnerAccountService.DISPLAY_NAME_MAX_LENGTH) {
993
+ throw new Error(
994
+ `displayName must be at most ${_PartnerAccountService.DISPLAY_NAME_MAX_LENGTH} characters`
995
+ );
996
+ }
997
+ this.logger.debug("Creating partner account", {
998
+ displayName: input.displayName,
999
+ createServerWallet: input.createServerWallet
1000
+ });
1001
+ const payload = {
1002
+ displayName: input.displayName,
1003
+ createServerWallet: input.createServerWallet
1004
+ };
1005
+ return this.httpClient.postWithHeaders(
1006
+ "/profiles/partner-accounts",
1007
+ payload,
1008
+ eoaHeaders ? {
1009
+ "x-account": eoaHeaders.account,
1010
+ "x-signing-message": eoaHeaders.signingMessage,
1011
+ "x-signature": eoaHeaders.signature
1012
+ } : void 0
1013
+ );
1014
+ }
1015
+ };
1016
+ _PartnerAccountService.DISPLAY_NAME_MAX_LENGTH = 44;
1017
+ var PartnerAccountService = _PartnerAccountService;
1018
+
725
1019
  // src/orders/builder.ts
726
1020
  var import_ethers = require("ethers");
727
1021
  var ZERO_ADDRESS2 = "0x0000000000000000000000000000000000000000";
@@ -1010,6 +1304,104 @@ var OrderBuilder = class {
1010
1304
  }
1011
1305
  };
1012
1306
 
1307
+ // src/delegated-orders/service.ts
1308
+ var DEFAULT_DELEGATED_FEE_RATE_BPS = 300;
1309
+ var DelegatedOrderService = class {
1310
+ constructor(httpClient, logger) {
1311
+ this.httpClient = httpClient;
1312
+ this.logger = logger || new NoOpLogger();
1313
+ }
1314
+ async createOrder(params) {
1315
+ this.httpClient.requireAuth("createDelegatedOrder");
1316
+ if (!Number.isInteger(params.onBehalfOf) || params.onBehalfOf <= 0) {
1317
+ throw new Error("onBehalfOf must be a positive integer");
1318
+ }
1319
+ const feeRateBps = params.feeRateBps && params.feeRateBps > 0 ? params.feeRateBps : DEFAULT_DELEGATED_FEE_RATE_BPS;
1320
+ const builder = new OrderBuilder(ZERO_ADDRESS, feeRateBps);
1321
+ const unsignedOrder = builder.buildOrder(params.args);
1322
+ const payload = {
1323
+ order: {
1324
+ salt: unsignedOrder.salt,
1325
+ maker: unsignedOrder.maker,
1326
+ signer: unsignedOrder.signer,
1327
+ taker: unsignedOrder.taker,
1328
+ tokenId: unsignedOrder.tokenId,
1329
+ makerAmount: unsignedOrder.makerAmount,
1330
+ takerAmount: unsignedOrder.takerAmount,
1331
+ expiration: unsignedOrder.expiration,
1332
+ nonce: unsignedOrder.nonce,
1333
+ feeRateBps: unsignedOrder.feeRateBps,
1334
+ side: unsignedOrder.side,
1335
+ signatureType: 0 /* EOA */,
1336
+ ...unsignedOrder.price !== void 0 ? { price: unsignedOrder.price } : {}
1337
+ },
1338
+ orderType: params.orderType,
1339
+ marketSlug: params.marketSlug,
1340
+ ownerId: params.onBehalfOf,
1341
+ onBehalfOf: params.onBehalfOf
1342
+ };
1343
+ this.logger.debug("Creating delegated order", {
1344
+ marketSlug: params.marketSlug,
1345
+ onBehalfOf: params.onBehalfOf,
1346
+ feeRateBps
1347
+ });
1348
+ return this.httpClient.post("/orders", payload);
1349
+ }
1350
+ async cancel(orderId) {
1351
+ this.httpClient.requireAuth("cancelDelegatedOrder");
1352
+ const response = await this.httpClient.delete(`/orders/${encodeURIComponent(orderId)}`);
1353
+ return response.message;
1354
+ }
1355
+ async cancelOnBehalfOf(orderId, onBehalfOf) {
1356
+ this.httpClient.requireAuth("cancelDelegatedOrder");
1357
+ if (!Number.isInteger(onBehalfOf) || onBehalfOf <= 0) {
1358
+ throw new Error("onBehalfOf must be a positive integer");
1359
+ }
1360
+ const response = await this.httpClient.delete(
1361
+ `/orders/${encodeURIComponent(orderId)}?onBehalfOf=${onBehalfOf}`
1362
+ );
1363
+ return response.message;
1364
+ }
1365
+ async cancelAll(marketSlug) {
1366
+ this.httpClient.requireAuth("cancelAllDelegatedOrders");
1367
+ const response = await this.httpClient.delete(`/orders/all/${encodeURIComponent(marketSlug)}`);
1368
+ return response.message;
1369
+ }
1370
+ async cancelAllOnBehalfOf(marketSlug, onBehalfOf) {
1371
+ this.httpClient.requireAuth("cancelAllDelegatedOrders");
1372
+ if (!Number.isInteger(onBehalfOf) || onBehalfOf <= 0) {
1373
+ throw new Error("onBehalfOf must be a positive integer");
1374
+ }
1375
+ const response = await this.httpClient.delete(
1376
+ `/orders/all/${encodeURIComponent(marketSlug)}?onBehalfOf=${onBehalfOf}`
1377
+ );
1378
+ return response.message;
1379
+ }
1380
+ };
1381
+
1382
+ // src/utils/number-flex.ts
1383
+ function toFiniteNumber(value) {
1384
+ if (typeof value === "number") {
1385
+ return Number.isFinite(value) ? value : void 0;
1386
+ }
1387
+ if (typeof value === "string") {
1388
+ const trimmed = value.trim();
1389
+ if (trimmed.length === 0) {
1390
+ return void 0;
1391
+ }
1392
+ const parsed = Number(trimmed);
1393
+ return Number.isFinite(parsed) ? parsed : void 0;
1394
+ }
1395
+ return void 0;
1396
+ }
1397
+ function toFiniteInteger(value) {
1398
+ const parsed = toFiniteNumber(value);
1399
+ if (parsed === void 0) {
1400
+ return void 0;
1401
+ }
1402
+ return Number.isSafeInteger(parsed) ? parsed : void 0;
1403
+ }
1404
+
1013
1405
  // src/orders/signer.ts
1014
1406
  var OrderSigner = class {
1015
1407
  /**
@@ -1673,9 +2065,14 @@ var OrderClient = class {
1673
2065
  });
1674
2066
  const portfolioFetcher = new PortfolioFetcher(this.httpClient);
1675
2067
  const profile = await portfolioFetcher.getProfile(this.wallet.address);
2068
+ const userId = toFiniteInteger(profile.id);
2069
+ const feeRateBps = toFiniteInteger(profile.rank?.feeRateBps) ?? 300;
2070
+ if (userId === void 0) {
2071
+ throw new Error(`Invalid user profile id: ${profile.id}`);
2072
+ }
1676
2073
  this.cachedUserData = {
1677
- userId: profile.id,
1678
- feeRateBps: profile.rank?.feeRateBps || 300
2074
+ userId,
2075
+ feeRateBps
1679
2076
  };
1680
2077
  this.orderBuilder = new OrderBuilder(
1681
2078
  this.wallet.address,
@@ -1787,26 +2184,27 @@ var OrderClient = class {
1787
2184
  * @internal
1788
2185
  */
1789
2186
  transformOrderResponse(apiResponse) {
2187
+ const order = apiResponse.order;
1790
2188
  const cleanOrder = {
1791
2189
  order: {
1792
- id: apiResponse.order.id,
1793
- createdAt: apiResponse.order.createdAt,
1794
- makerAmount: apiResponse.order.makerAmount,
1795
- takerAmount: apiResponse.order.takerAmount,
1796
- expiration: apiResponse.order.expiration,
1797
- signatureType: apiResponse.order.signatureType,
1798
- salt: apiResponse.order.salt,
1799
- maker: apiResponse.order.maker,
1800
- signer: apiResponse.order.signer,
1801
- taker: apiResponse.order.taker,
1802
- tokenId: apiResponse.order.tokenId,
1803
- side: apiResponse.order.side,
1804
- feeRateBps: apiResponse.order.feeRateBps,
1805
- nonce: apiResponse.order.nonce,
1806
- signature: apiResponse.order.signature,
1807
- orderType: apiResponse.order.orderType,
1808
- price: apiResponse.order.price,
1809
- marketId: apiResponse.order.marketId
2190
+ id: order.id,
2191
+ createdAt: order.createdAt,
2192
+ makerAmount: toFiniteNumber(order.makerAmount) ?? order.makerAmount,
2193
+ takerAmount: toFiniteNumber(order.takerAmount) ?? order.takerAmount,
2194
+ expiration: order.expiration,
2195
+ signatureType: order.signatureType,
2196
+ salt: toFiniteInteger(order.salt) ?? order.salt,
2197
+ maker: order.maker,
2198
+ signer: order.signer,
2199
+ taker: order.taker,
2200
+ tokenId: order.tokenId,
2201
+ side: order.side,
2202
+ feeRateBps: order.feeRateBps,
2203
+ nonce: order.nonce,
2204
+ signature: order.signature,
2205
+ orderType: order.orderType,
2206
+ price: order.price === void 0 || order.price === null ? order.price : toFiniteNumber(order.price) ?? order.price,
2207
+ marketId: order.marketId
1810
2208
  }
1811
2209
  };
1812
2210
  if (apiResponse.makerMatches && apiResponse.makerMatches.length > 0) {
@@ -2125,6 +2523,7 @@ var WebSocketClient = class {
2125
2523
  this.config = {
2126
2524
  url: config.url || DEFAULT_WS_URL,
2127
2525
  apiKey: config.apiKey || process.env.LIMITLESS_API_KEY || "",
2526
+ hmacCredentials: config.hmacCredentials,
2128
2527
  autoReconnect: config.autoReconnect ?? true,
2129
2528
  reconnectDelay: config.reconnectDelay || 1e3,
2130
2529
  maxReconnectAttempts: config.maxReconnectAttempts || Infinity,
@@ -2164,6 +2563,32 @@ var WebSocketClient = class {
2164
2563
  this.reconnectWithNewAuth();
2165
2564
  }
2166
2565
  }
2566
+ /**
2567
+ * Sets HMAC credentials for authenticated subscriptions.
2568
+ *
2569
+ * @remarks
2570
+ * When configured alongside `apiKey`, this client uses HMAC headers for authenticated subscriptions.
2571
+ */
2572
+ setHMACCredentials(hmacCredentials) {
2573
+ this.config.hmacCredentials = {
2574
+ tokenId: hmacCredentials.tokenId,
2575
+ secret: hmacCredentials.secret
2576
+ };
2577
+ if (this.socket?.connected) {
2578
+ this.logger.info("HMAC credentials updated, reconnecting...");
2579
+ this.reconnectWithNewAuth();
2580
+ }
2581
+ }
2582
+ /**
2583
+ * Clears HMAC credentials.
2584
+ */
2585
+ clearHMACCredentials() {
2586
+ this.config.hmacCredentials = void 0;
2587
+ if (this.socket?.connected) {
2588
+ this.logger.info("HMAC credentials cleared, reconnecting...");
2589
+ this.reconnectWithNewAuth();
2590
+ }
2591
+ }
2167
2592
  /**
2168
2593
  * Reconnects with new authentication credentials.
2169
2594
  * @internal
@@ -2209,10 +2634,29 @@ var WebSocketClient = class {
2209
2634
  // Add jitter to prevent thundering herd
2210
2635
  timeout: this.config.timeout
2211
2636
  };
2212
- if (this.config.apiKey) {
2637
+ const extraHeaders = buildWebSocketTrackingHeaders();
2638
+ if (this.config.hmacCredentials) {
2639
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
2640
+ const signature = computeHMACSignature(
2641
+ this.config.hmacCredentials.secret,
2642
+ timestamp,
2643
+ "GET",
2644
+ "/socket.io/?EIO=4&transport=websocket",
2645
+ ""
2646
+ );
2213
2647
  socketOptions.extraHeaders = {
2648
+ ...extraHeaders,
2649
+ "lmts-api-key": this.config.hmacCredentials.tokenId,
2650
+ "lmts-timestamp": timestamp,
2651
+ "lmts-signature": signature
2652
+ };
2653
+ } else if (this.config.apiKey) {
2654
+ socketOptions.extraHeaders = {
2655
+ ...extraHeaders,
2214
2656
  "X-API-Key": this.config.apiKey
2215
2657
  };
2658
+ } else if (Object.keys(extraHeaders).length > 0) {
2659
+ socketOptions.extraHeaders = extraHeaders;
2216
2660
  }
2217
2661
  this.socket = (0, import_socket.io)(wsUrl + "/markets", socketOptions);
2218
2662
  this.attachPendingListeners();
@@ -2281,9 +2725,9 @@ var WebSocketClient = class {
2281
2725
  "subscribe_positions",
2282
2726
  "subscribe_transactions"
2283
2727
  ];
2284
- if (authenticatedChannels.includes(channel) && !this.config.apiKey) {
2728
+ if (authenticatedChannels.includes(channel) && !this.config.apiKey && !this.config.hmacCredentials) {
2285
2729
  throw new Error(
2286
- `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`
2730
+ `Authentication is required for '${channel}' subscription. Please provide either apiKey or hmacCredentials when creating the WebSocket client.`
2287
2731
  );
2288
2732
  }
2289
2733
  const subscriptionKey = this.getSubscriptionKey(channel, options);
@@ -2477,16 +2921,75 @@ var WebSocketClient = class {
2477
2921
  return key.split(":")[0];
2478
2922
  }
2479
2923
  };
2924
+
2925
+ // src/client.ts
2926
+ var import_ethers3 = require("ethers");
2927
+ var Client = class _Client {
2928
+ constructor(config = {}) {
2929
+ this.http = new HttpClient(config);
2930
+ const logger = this.http.getLogger?.() || new NoOpLogger();
2931
+ this.markets = new MarketFetcher(this.http, logger);
2932
+ this.portfolio = new PortfolioFetcher(this.http, logger);
2933
+ this.pages = new MarketPageFetcher(this.http, logger);
2934
+ this.apiTokens = new ApiTokenService(this.http, logger);
2935
+ this.partnerAccounts = new PartnerAccountService(this.http, logger);
2936
+ this.delegatedOrders = new DelegatedOrderService(this.http, logger);
2937
+ }
2938
+ /**
2939
+ * Creates a root client around an existing shared HTTP client.
2940
+ */
2941
+ static fromHttpClient(httpClient) {
2942
+ const client = Object.create(_Client.prototype);
2943
+ const logger = httpClient.getLogger?.() || new NoOpLogger();
2944
+ client.http = httpClient;
2945
+ client.markets = new MarketFetcher(httpClient, logger);
2946
+ client.portfolio = new PortfolioFetcher(httpClient, logger);
2947
+ client.pages = new MarketPageFetcher(httpClient, logger);
2948
+ client.apiTokens = new ApiTokenService(httpClient, logger);
2949
+ client.partnerAccounts = new PartnerAccountService(httpClient, logger);
2950
+ client.delegatedOrders = new DelegatedOrderService(httpClient, logger);
2951
+ return client;
2952
+ }
2953
+ /**
2954
+ * Creates a regular EIP-712 order client reusing the shared transport and market cache.
2955
+ */
2956
+ newOrderClient(walletOrPrivateKey, config = {}) {
2957
+ const wallet = typeof walletOrPrivateKey === "string" ? new import_ethers3.ethers.Wallet(walletOrPrivateKey) : walletOrPrivateKey;
2958
+ return new OrderClient({
2959
+ httpClient: this.http,
2960
+ wallet,
2961
+ marketFetcher: this.markets,
2962
+ logger: this.http.getLogger(),
2963
+ ...config
2964
+ });
2965
+ }
2966
+ /**
2967
+ * Creates a WebSocket client reusing shared auth where possible.
2968
+ */
2969
+ newWebSocketClient(config = {}) {
2970
+ return new WebSocketClient(
2971
+ {
2972
+ apiKey: config.apiKey || this.http.getApiKey(),
2973
+ hmacCredentials: config.hmacCredentials || this.http.getHMACCredentials(),
2974
+ ...config
2975
+ },
2976
+ this.http.getLogger()
2977
+ );
2978
+ }
2979
+ };
2480
2980
  // Annotate the CommonJS export names for ESM import in node:
2481
2981
  0 && (module.exports = {
2482
2982
  APIError,
2983
+ ApiTokenService,
2483
2984
  AuthenticationError,
2484
2985
  BASE_SEPOLIA_CHAIN_ID,
2485
2986
  CONTRACT_ADDRESSES,
2987
+ Client,
2486
2988
  ConsoleLogger,
2487
2989
  DEFAULT_API_URL,
2488
2990
  DEFAULT_CHAIN_ID,
2489
2991
  DEFAULT_WS_URL,
2992
+ DelegatedOrderService,
2490
2993
  HttpClient,
2491
2994
  Market,
2492
2995
  MarketFetcher,
@@ -2497,19 +3000,27 @@ var WebSocketClient = class {
2497
3000
  OrderSigner,
2498
3001
  OrderType,
2499
3002
  OrderValidationError,
3003
+ PartnerAccountService,
2500
3004
  PortfolioFetcher,
2501
3005
  RateLimitError,
2502
3006
  RetryConfig,
2503
3007
  RetryableClient,
2504
3008
  SIGNING_MESSAGE_TEMPLATE,
3009
+ ScopeAccountCreation,
3010
+ ScopeDelegatedSigning,
3011
+ ScopeTrading,
2505
3012
  Side,
2506
3013
  SignatureType,
2507
3014
  ValidationError,
2508
3015
  WebSocketClient,
2509
3016
  WebSocketState,
2510
3017
  ZERO_ADDRESS,
3018
+ buildHMACMessage,
3019
+ computeHMACSignature,
2511
3020
  getContractAddress,
2512
3021
  retryOnErrors,
3022
+ toFiniteInteger,
3023
+ toFiniteNumber,
2513
3024
  validateOrderArgs,
2514
3025
  validateSignedOrder,
2515
3026
  validateUnsignedOrder,