@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/README.md +105 -33
- package/dist/index.d.mts +479 -51
- package/dist/index.d.ts +479 -51
- package/dist/index.js +550 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +539 -34
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
-
"
|
|
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
|
-
(
|
|
357
|
-
|
|
358
|
-
|
|
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 =
|
|
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
|
|
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
|
|
1678
|
-
feeRateBps
|
|
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:
|
|
1793
|
-
createdAt:
|
|
1794
|
-
makerAmount:
|
|
1795
|
-
takerAmount:
|
|
1796
|
-
expiration:
|
|
1797
|
-
signatureType:
|
|
1798
|
-
salt:
|
|
1799
|
-
maker:
|
|
1800
|
-
signer:
|
|
1801
|
-
taker:
|
|
1802
|
-
tokenId:
|
|
1803
|
-
side:
|
|
1804
|
-
feeRateBps:
|
|
1805
|
-
nonce:
|
|
1806
|
-
signature:
|
|
1807
|
-
orderType:
|
|
1808
|
-
price:
|
|
1809
|
-
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
|
-
|
|
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
|
-
`
|
|
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,
|