@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/README.md +42 -24
- package/dist/index.d.mts +433 -50
- package/dist/index.d.ts +433 -50
- package/dist/index.js +544 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +533 -33
- 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() {
|
|
@@ -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
|
-
"
|
|
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
|
-
(
|
|
357
|
-
|
|
358
|
-
|
|
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 =
|
|
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
|
|
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
|
|
1678
|
-
feeRateBps
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
`
|
|
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,
|