@limitless-exchange/sdk 1.0.3 → 1.0.5

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