@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.mjs
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// src/types/api-tokens.ts
|
|
2
|
+
var ScopeTrading = "trading";
|
|
3
|
+
var ScopeAccountCreation = "account_creation";
|
|
4
|
+
var ScopeDelegatedSigning = "delegated_signing";
|
|
5
|
+
|
|
1
6
|
// src/types/logger.ts
|
|
2
7
|
var NoOpLogger = class {
|
|
3
8
|
debug() {
|
|
@@ -48,6 +53,7 @@ var Side = /* @__PURE__ */ ((Side2) => {
|
|
|
48
53
|
})(Side || {});
|
|
49
54
|
var OrderType = /* @__PURE__ */ ((OrderType2) => {
|
|
50
55
|
OrderType2["FOK"] = "FOK";
|
|
56
|
+
OrderType2["FAK"] = "FAK";
|
|
51
57
|
OrderType2["GTC"] = "GTC";
|
|
52
58
|
return OrderType2;
|
|
53
59
|
})(OrderType || {});
|
|
@@ -155,6 +161,40 @@ function getContractAddress(contractType, chainId = DEFAULT_CHAIN_ID) {
|
|
|
155
161
|
return address;
|
|
156
162
|
}
|
|
157
163
|
|
|
164
|
+
// src/utils/sdk-tracking.ts
|
|
165
|
+
var SDK_ID = "lmts-sdk-ts";
|
|
166
|
+
function isNodeRuntime() {
|
|
167
|
+
return typeof process !== "undefined" && !!process.versions?.node;
|
|
168
|
+
}
|
|
169
|
+
function resolveSdkVersion() {
|
|
170
|
+
if ("1.0.5") {
|
|
171
|
+
return "1.0.5";
|
|
172
|
+
}
|
|
173
|
+
return "0.0.0";
|
|
174
|
+
}
|
|
175
|
+
function resolveRuntimeToken() {
|
|
176
|
+
if (isNodeRuntime()) {
|
|
177
|
+
return `node/${process.versions.node}`;
|
|
178
|
+
}
|
|
179
|
+
return "runtime/unknown";
|
|
180
|
+
}
|
|
181
|
+
function buildSdkTrackingHeaders() {
|
|
182
|
+
const sdkVersion = resolveSdkVersion();
|
|
183
|
+
const headers = {
|
|
184
|
+
"x-sdk-version": `${SDK_ID}/${sdkVersion}`
|
|
185
|
+
};
|
|
186
|
+
if (isNodeRuntime()) {
|
|
187
|
+
headers["user-agent"] = `${SDK_ID}/${sdkVersion} (${resolveRuntimeToken()})`;
|
|
188
|
+
}
|
|
189
|
+
return headers;
|
|
190
|
+
}
|
|
191
|
+
function buildWebSocketTrackingHeaders() {
|
|
192
|
+
if (!isNodeRuntime()) {
|
|
193
|
+
return {};
|
|
194
|
+
}
|
|
195
|
+
return buildSdkTrackingHeaders();
|
|
196
|
+
}
|
|
197
|
+
|
|
158
198
|
// src/api/errors.ts
|
|
159
199
|
var APIError = class _APIError extends Error {
|
|
160
200
|
/**
|
|
@@ -227,6 +267,19 @@ var ValidationError = class _ValidationError extends APIError {
|
|
|
227
267
|
}
|
|
228
268
|
};
|
|
229
269
|
|
|
270
|
+
// src/api/hmac.ts
|
|
271
|
+
import { createHmac } from "crypto";
|
|
272
|
+
function buildHMACMessage(timestamp, method, path, body) {
|
|
273
|
+
return `${timestamp}
|
|
274
|
+
${method.toUpperCase()}
|
|
275
|
+
${path}
|
|
276
|
+
${body}`;
|
|
277
|
+
}
|
|
278
|
+
function computeHMACSignature(secret, timestamp, method, path, body) {
|
|
279
|
+
const key = Buffer.from(secret, "base64");
|
|
280
|
+
return createHmac("sha256", key).update(buildHMACMessage(timestamp, method, path, body)).digest("base64");
|
|
281
|
+
}
|
|
282
|
+
|
|
230
283
|
// src/api/http.ts
|
|
231
284
|
var HttpClient = class {
|
|
232
285
|
/**
|
|
@@ -236,10 +289,14 @@ var HttpClient = class {
|
|
|
236
289
|
*/
|
|
237
290
|
constructor(config = {}) {
|
|
238
291
|
this.apiKey = config.apiKey || process.env.LIMITLESS_API_KEY;
|
|
292
|
+
this.hmacCredentials = config.hmacCredentials ? {
|
|
293
|
+
tokenId: config.hmacCredentials.tokenId,
|
|
294
|
+
secret: config.hmacCredentials.secret
|
|
295
|
+
} : void 0;
|
|
239
296
|
this.logger = config.logger || new NoOpLogger();
|
|
240
|
-
if (!this.apiKey) {
|
|
297
|
+
if (!this.apiKey && !this.hmacCredentials) {
|
|
241
298
|
this.logger.warn(
|
|
242
|
-
"
|
|
299
|
+
"Authentication not set. Authenticated endpoints will fail. Set LIMITLESS_API_KEY environment variable, pass apiKey, or configure hmacCredentials."
|
|
243
300
|
);
|
|
244
301
|
}
|
|
245
302
|
const keepAlive = config.keepAlive !== false;
|
|
@@ -266,6 +323,7 @@ var HttpClient = class {
|
|
|
266
323
|
headers: {
|
|
267
324
|
"Content-Type": "application/json",
|
|
268
325
|
Accept: "application/json",
|
|
326
|
+
...buildSdkTrackingHeaders(),
|
|
269
327
|
...config.additionalHeaders
|
|
270
328
|
}
|
|
271
329
|
});
|
|
@@ -283,21 +341,42 @@ var HttpClient = class {
|
|
|
283
341
|
*/
|
|
284
342
|
setupInterceptors() {
|
|
285
343
|
this.client.interceptors.request.use(
|
|
286
|
-
(
|
|
287
|
-
|
|
288
|
-
|
|
344
|
+
(rawConfig) => {
|
|
345
|
+
const config = rawConfig;
|
|
346
|
+
const headers = config.headers || (config.headers = {});
|
|
347
|
+
const identityToken = config.identityToken;
|
|
348
|
+
delete headers["X-API-Key"];
|
|
349
|
+
delete headers["lmts-api-key"];
|
|
350
|
+
delete headers["lmts-timestamp"];
|
|
351
|
+
delete headers["lmts-signature"];
|
|
352
|
+
delete headers.identity;
|
|
353
|
+
if (identityToken) {
|
|
354
|
+
headers.identity = `Bearer ${identityToken}`;
|
|
355
|
+
} else if (this.hmacCredentials) {
|
|
356
|
+
const requestPath = this.getRequestPath(config);
|
|
357
|
+
const requestBody = this.getRequestBodyForSignature(config.data);
|
|
358
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
359
|
+
const signature = computeHMACSignature(
|
|
360
|
+
this.hmacCredentials.secret,
|
|
361
|
+
timestamp,
|
|
362
|
+
config.method || "GET",
|
|
363
|
+
requestPath,
|
|
364
|
+
requestBody
|
|
365
|
+
);
|
|
366
|
+
headers["lmts-api-key"] = this.hmacCredentials.tokenId;
|
|
367
|
+
headers["lmts-timestamp"] = timestamp;
|
|
368
|
+
headers["lmts-signature"] = signature;
|
|
369
|
+
} else if (this.apiKey) {
|
|
370
|
+
headers["X-API-Key"] = this.apiKey;
|
|
289
371
|
}
|
|
290
372
|
const fullUrl = `${config.baseURL || ""}${config.url || ""}`;
|
|
291
373
|
const method = config.method?.toUpperCase() || "GET";
|
|
292
|
-
const logHeaders =
|
|
293
|
-
if (logHeaders["X-API-Key"]) {
|
|
294
|
-
logHeaders["X-API-Key"] = "***";
|
|
295
|
-
}
|
|
374
|
+
const logHeaders = this.maskSensitiveHeaders(headers);
|
|
296
375
|
this.logger.debug(`\u2192 ${method} ${fullUrl}`, {
|
|
297
376
|
headers: logHeaders,
|
|
298
377
|
body: config.data
|
|
299
378
|
});
|
|
300
|
-
return
|
|
379
|
+
return rawConfig;
|
|
301
380
|
},
|
|
302
381
|
(error) => Promise.reject(error)
|
|
303
382
|
);
|
|
@@ -396,12 +475,86 @@ var HttpClient = class {
|
|
|
396
475
|
setApiKey(apiKey) {
|
|
397
476
|
this.apiKey = apiKey;
|
|
398
477
|
}
|
|
478
|
+
/**
|
|
479
|
+
* Returns the configured API key, if any.
|
|
480
|
+
*/
|
|
481
|
+
getApiKey() {
|
|
482
|
+
return this.apiKey;
|
|
483
|
+
}
|
|
399
484
|
/**
|
|
400
485
|
* Clears the API key.
|
|
401
486
|
*/
|
|
402
487
|
clearApiKey() {
|
|
403
488
|
this.apiKey = void 0;
|
|
404
489
|
}
|
|
490
|
+
/**
|
|
491
|
+
* Sets HMAC credentials for scoped API-token authentication.
|
|
492
|
+
*/
|
|
493
|
+
setHMACCredentials(credentials) {
|
|
494
|
+
this.hmacCredentials = {
|
|
495
|
+
tokenId: credentials.tokenId,
|
|
496
|
+
secret: credentials.secret
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Clears HMAC credentials.
|
|
501
|
+
*/
|
|
502
|
+
clearHMACCredentials() {
|
|
503
|
+
this.hmacCredentials = void 0;
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Returns a copy of the configured HMAC credentials, if any.
|
|
507
|
+
*/
|
|
508
|
+
getHMACCredentials() {
|
|
509
|
+
if (!this.hmacCredentials) {
|
|
510
|
+
return void 0;
|
|
511
|
+
}
|
|
512
|
+
return {
|
|
513
|
+
tokenId: this.hmacCredentials.tokenId,
|
|
514
|
+
secret: this.hmacCredentials.secret
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Returns the logger attached to this HTTP client.
|
|
519
|
+
*/
|
|
520
|
+
getLogger() {
|
|
521
|
+
return this.logger;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Returns true when cookie/header-based auth is configured on the underlying client.
|
|
525
|
+
* This is primarily used for custom authenticated flows that don't use API keys or HMAC.
|
|
526
|
+
*/
|
|
527
|
+
hasConfiguredHeaderAuth() {
|
|
528
|
+
const defaultHeaders = this.client.defaults.headers;
|
|
529
|
+
const candidates = [defaultHeaders?.common, defaultHeaders];
|
|
530
|
+
for (const headers of candidates) {
|
|
531
|
+
if (!headers) {
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
const authValues = [
|
|
535
|
+
headers.Cookie,
|
|
536
|
+
headers.cookie,
|
|
537
|
+
headers.Authorization,
|
|
538
|
+
headers.authorization,
|
|
539
|
+
headers.identity
|
|
540
|
+
];
|
|
541
|
+
if (authValues.some((value) => typeof value === "string" && value.trim() !== "")) {
|
|
542
|
+
return true;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Ensures the client has some authenticated transport configured.
|
|
549
|
+
*/
|
|
550
|
+
requireAuth(operation) {
|
|
551
|
+
if (this.apiKey || this.hmacCredentials || this.hasConfiguredHeaderAuth()) {
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
throw new Error(
|
|
555
|
+
`Authentication is required for ${operation}; pass apiKey, hmacCredentials, cookie/auth headers, or set LIMITLESS_API_KEY.`
|
|
556
|
+
);
|
|
557
|
+
}
|
|
405
558
|
/**
|
|
406
559
|
* Performs a GET request.
|
|
407
560
|
*
|
|
@@ -413,6 +566,16 @@ var HttpClient = class {
|
|
|
413
566
|
const response = await this.client.get(url, config);
|
|
414
567
|
return response.data;
|
|
415
568
|
}
|
|
569
|
+
/**
|
|
570
|
+
* Performs a GET request with identity-token authentication.
|
|
571
|
+
*/
|
|
572
|
+
async getWithIdentity(url, identityToken, config) {
|
|
573
|
+
const response = await this.client.get(url, {
|
|
574
|
+
...config,
|
|
575
|
+
identityToken
|
|
576
|
+
});
|
|
577
|
+
return response.data;
|
|
578
|
+
}
|
|
416
579
|
/**
|
|
417
580
|
* Performs a GET request and returns raw response metadata.
|
|
418
581
|
*
|
|
@@ -447,6 +610,36 @@ var HttpClient = class {
|
|
|
447
610
|
const response = await this.client.post(url, data, config);
|
|
448
611
|
return response.data;
|
|
449
612
|
}
|
|
613
|
+
/**
|
|
614
|
+
* Performs a POST request with identity-token authentication.
|
|
615
|
+
*/
|
|
616
|
+
async postWithIdentity(url, identityToken, data, config) {
|
|
617
|
+
const response = await this.client.post(url, data, {
|
|
618
|
+
...config,
|
|
619
|
+
identityToken
|
|
620
|
+
});
|
|
621
|
+
return response.data;
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Performs a POST request with additional per-request headers.
|
|
625
|
+
*/
|
|
626
|
+
async postWithHeaders(url, data, headers, config) {
|
|
627
|
+
const response = await this.client.post(url, data, {
|
|
628
|
+
...config,
|
|
629
|
+
headers: {
|
|
630
|
+
...config?.headers || {},
|
|
631
|
+
...headers || {}
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
return response.data;
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Performs a PATCH request.
|
|
638
|
+
*/
|
|
639
|
+
async patch(url, data, config) {
|
|
640
|
+
const response = await this.client.patch(url, data, config);
|
|
641
|
+
return response.data;
|
|
642
|
+
}
|
|
450
643
|
/**
|
|
451
644
|
* Performs a DELETE request.
|
|
452
645
|
*
|
|
@@ -469,6 +662,28 @@ var HttpClient = class {
|
|
|
469
662
|
const response = await this.client.delete(url, deleteConfig);
|
|
470
663
|
return response.data;
|
|
471
664
|
}
|
|
665
|
+
getRequestPath(config) {
|
|
666
|
+
const resolved = new URL(config.url || "", config.baseURL || this.client.defaults.baseURL || DEFAULT_API_URL);
|
|
667
|
+
return `${resolved.pathname}${resolved.search}`;
|
|
668
|
+
}
|
|
669
|
+
getRequestBodyForSignature(data) {
|
|
670
|
+
if (data === void 0 || data === null || data === "") {
|
|
671
|
+
return "";
|
|
672
|
+
}
|
|
673
|
+
if (typeof data === "string") {
|
|
674
|
+
return data;
|
|
675
|
+
}
|
|
676
|
+
return JSON.stringify(data);
|
|
677
|
+
}
|
|
678
|
+
maskSensitiveHeaders(headers) {
|
|
679
|
+
const masked = { ...headers };
|
|
680
|
+
for (const key of ["X-API-Key", "lmts-api-key", "lmts-timestamp", "lmts-signature", "identity"]) {
|
|
681
|
+
if (masked[key] !== void 0) {
|
|
682
|
+
masked[key] = key === "identity" ? "Bearer ***" : "***";
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return masked;
|
|
686
|
+
}
|
|
472
687
|
};
|
|
473
688
|
|
|
474
689
|
// src/api/retry.ts
|
|
@@ -652,6 +867,75 @@ var RetryableClient = class {
|
|
|
652
867
|
}
|
|
653
868
|
};
|
|
654
869
|
|
|
870
|
+
// src/api-tokens/service.ts
|
|
871
|
+
var ApiTokenService = class {
|
|
872
|
+
constructor(httpClient, logger) {
|
|
873
|
+
this.httpClient = httpClient;
|
|
874
|
+
this.logger = logger || new NoOpLogger();
|
|
875
|
+
}
|
|
876
|
+
async deriveToken(identityToken, input) {
|
|
877
|
+
if (!identityToken) {
|
|
878
|
+
throw new Error("Identity token is required for deriveToken");
|
|
879
|
+
}
|
|
880
|
+
this.logger.debug("Deriving API token", { scopes: input.scopes, label: input.label });
|
|
881
|
+
return this.httpClient.postWithIdentity("/auth/api-tokens/derive", identityToken, input);
|
|
882
|
+
}
|
|
883
|
+
async listTokens() {
|
|
884
|
+
this.httpClient.requireAuth("listTokens");
|
|
885
|
+
return this.httpClient.get("/auth/api-tokens");
|
|
886
|
+
}
|
|
887
|
+
async getCapabilities(identityToken) {
|
|
888
|
+
if (!identityToken) {
|
|
889
|
+
throw new Error("Identity token is required for getCapabilities");
|
|
890
|
+
}
|
|
891
|
+
return this.httpClient.getWithIdentity("/auth/api-tokens/capabilities", identityToken);
|
|
892
|
+
}
|
|
893
|
+
async revokeToken(tokenId) {
|
|
894
|
+
this.httpClient.requireAuth("revokeToken");
|
|
895
|
+
const response = await this.httpClient.delete(`/auth/api-tokens/${encodeURIComponent(tokenId)}`);
|
|
896
|
+
return response.message;
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
|
|
900
|
+
// src/partner-accounts/service.ts
|
|
901
|
+
var _PartnerAccountService = class _PartnerAccountService {
|
|
902
|
+
constructor(httpClient, logger) {
|
|
903
|
+
this.httpClient = httpClient;
|
|
904
|
+
this.logger = logger || new NoOpLogger();
|
|
905
|
+
}
|
|
906
|
+
async createAccount(input, eoaHeaders) {
|
|
907
|
+
this.httpClient.requireAuth("createPartnerAccount");
|
|
908
|
+
const serverWalletMode = input.createServerWallet === true;
|
|
909
|
+
if (!serverWalletMode && !eoaHeaders) {
|
|
910
|
+
throw new Error("EOA headers are required when createServerWallet is not true");
|
|
911
|
+
}
|
|
912
|
+
if (input.displayName && input.displayName.length > _PartnerAccountService.DISPLAY_NAME_MAX_LENGTH) {
|
|
913
|
+
throw new Error(
|
|
914
|
+
`displayName must be at most ${_PartnerAccountService.DISPLAY_NAME_MAX_LENGTH} characters`
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
this.logger.debug("Creating partner account", {
|
|
918
|
+
displayName: input.displayName,
|
|
919
|
+
createServerWallet: input.createServerWallet
|
|
920
|
+
});
|
|
921
|
+
const payload = {
|
|
922
|
+
displayName: input.displayName,
|
|
923
|
+
createServerWallet: input.createServerWallet
|
|
924
|
+
};
|
|
925
|
+
return this.httpClient.postWithHeaders(
|
|
926
|
+
"/profiles/partner-accounts",
|
|
927
|
+
payload,
|
|
928
|
+
eoaHeaders ? {
|
|
929
|
+
"x-account": eoaHeaders.account,
|
|
930
|
+
"x-signing-message": eoaHeaders.signingMessage,
|
|
931
|
+
"x-signature": eoaHeaders.signature
|
|
932
|
+
} : void 0
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
_PartnerAccountService.DISPLAY_NAME_MAX_LENGTH = 44;
|
|
937
|
+
var PartnerAccountService = _PartnerAccountService;
|
|
938
|
+
|
|
655
939
|
// src/orders/builder.ts
|
|
656
940
|
import { ethers } from "ethers";
|
|
657
941
|
var ZERO_ADDRESS2 = "0x0000000000000000000000000000000000000000";
|
|
@@ -940,6 +1224,106 @@ var OrderBuilder = class {
|
|
|
940
1224
|
}
|
|
941
1225
|
};
|
|
942
1226
|
|
|
1227
|
+
// src/delegated-orders/service.ts
|
|
1228
|
+
var DEFAULT_DELEGATED_FEE_RATE_BPS = 300;
|
|
1229
|
+
var DelegatedOrderService = class {
|
|
1230
|
+
constructor(httpClient, logger) {
|
|
1231
|
+
this.httpClient = httpClient;
|
|
1232
|
+
this.logger = logger || new NoOpLogger();
|
|
1233
|
+
}
|
|
1234
|
+
async createOrder(params) {
|
|
1235
|
+
this.httpClient.requireAuth("createDelegatedOrder");
|
|
1236
|
+
if (!Number.isInteger(params.onBehalfOf) || params.onBehalfOf <= 0) {
|
|
1237
|
+
throw new Error("onBehalfOf must be a positive integer");
|
|
1238
|
+
}
|
|
1239
|
+
const feeRateBps = params.feeRateBps && params.feeRateBps > 0 ? params.feeRateBps : DEFAULT_DELEGATED_FEE_RATE_BPS;
|
|
1240
|
+
const builder = new OrderBuilder(ZERO_ADDRESS, feeRateBps);
|
|
1241
|
+
const unsignedOrder = builder.buildOrder(params.args);
|
|
1242
|
+
const postOnly = params.orderType === "GTC" /* GTC */ && "postOnly" in params.args && params.args.postOnly !== void 0 ? params.args.postOnly : void 0;
|
|
1243
|
+
const payload = {
|
|
1244
|
+
order: {
|
|
1245
|
+
salt: unsignedOrder.salt,
|
|
1246
|
+
maker: unsignedOrder.maker,
|
|
1247
|
+
signer: unsignedOrder.signer,
|
|
1248
|
+
taker: unsignedOrder.taker,
|
|
1249
|
+
tokenId: unsignedOrder.tokenId,
|
|
1250
|
+
makerAmount: unsignedOrder.makerAmount,
|
|
1251
|
+
takerAmount: unsignedOrder.takerAmount,
|
|
1252
|
+
expiration: unsignedOrder.expiration,
|
|
1253
|
+
nonce: unsignedOrder.nonce,
|
|
1254
|
+
feeRateBps: unsignedOrder.feeRateBps,
|
|
1255
|
+
side: unsignedOrder.side,
|
|
1256
|
+
signatureType: 0 /* EOA */,
|
|
1257
|
+
...unsignedOrder.price !== void 0 ? { price: unsignedOrder.price } : {}
|
|
1258
|
+
},
|
|
1259
|
+
orderType: params.orderType,
|
|
1260
|
+
marketSlug: params.marketSlug,
|
|
1261
|
+
ownerId: params.onBehalfOf,
|
|
1262
|
+
onBehalfOf: params.onBehalfOf,
|
|
1263
|
+
...postOnly !== void 0 ? { postOnly } : {}
|
|
1264
|
+
};
|
|
1265
|
+
this.logger.debug("Creating delegated order", {
|
|
1266
|
+
marketSlug: params.marketSlug,
|
|
1267
|
+
onBehalfOf: params.onBehalfOf,
|
|
1268
|
+
feeRateBps
|
|
1269
|
+
});
|
|
1270
|
+
return this.httpClient.post("/orders", payload);
|
|
1271
|
+
}
|
|
1272
|
+
async cancel(orderId) {
|
|
1273
|
+
this.httpClient.requireAuth("cancelDelegatedOrder");
|
|
1274
|
+
const response = await this.httpClient.delete(`/orders/${encodeURIComponent(orderId)}`);
|
|
1275
|
+
return response.message;
|
|
1276
|
+
}
|
|
1277
|
+
async cancelOnBehalfOf(orderId, onBehalfOf) {
|
|
1278
|
+
this.httpClient.requireAuth("cancelDelegatedOrder");
|
|
1279
|
+
if (!Number.isInteger(onBehalfOf) || onBehalfOf <= 0) {
|
|
1280
|
+
throw new Error("onBehalfOf must be a positive integer");
|
|
1281
|
+
}
|
|
1282
|
+
const response = await this.httpClient.delete(
|
|
1283
|
+
`/orders/${encodeURIComponent(orderId)}?onBehalfOf=${onBehalfOf}`
|
|
1284
|
+
);
|
|
1285
|
+
return response.message;
|
|
1286
|
+
}
|
|
1287
|
+
async cancelAll(marketSlug) {
|
|
1288
|
+
this.httpClient.requireAuth("cancelAllDelegatedOrders");
|
|
1289
|
+
const response = await this.httpClient.delete(`/orders/all/${encodeURIComponent(marketSlug)}`);
|
|
1290
|
+
return response.message;
|
|
1291
|
+
}
|
|
1292
|
+
async cancelAllOnBehalfOf(marketSlug, onBehalfOf) {
|
|
1293
|
+
this.httpClient.requireAuth("cancelAllDelegatedOrders");
|
|
1294
|
+
if (!Number.isInteger(onBehalfOf) || onBehalfOf <= 0) {
|
|
1295
|
+
throw new Error("onBehalfOf must be a positive integer");
|
|
1296
|
+
}
|
|
1297
|
+
const response = await this.httpClient.delete(
|
|
1298
|
+
`/orders/all/${encodeURIComponent(marketSlug)}?onBehalfOf=${onBehalfOf}`
|
|
1299
|
+
);
|
|
1300
|
+
return response.message;
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
|
|
1304
|
+
// src/utils/number-flex.ts
|
|
1305
|
+
function toFiniteNumber(value) {
|
|
1306
|
+
if (typeof value === "number") {
|
|
1307
|
+
return Number.isFinite(value) ? value : void 0;
|
|
1308
|
+
}
|
|
1309
|
+
if (typeof value === "string") {
|
|
1310
|
+
const trimmed = value.trim();
|
|
1311
|
+
if (trimmed.length === 0) {
|
|
1312
|
+
return void 0;
|
|
1313
|
+
}
|
|
1314
|
+
const parsed = Number(trimmed);
|
|
1315
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
1316
|
+
}
|
|
1317
|
+
return void 0;
|
|
1318
|
+
}
|
|
1319
|
+
function toFiniteInteger(value) {
|
|
1320
|
+
const parsed = toFiniteNumber(value);
|
|
1321
|
+
if (parsed === void 0) {
|
|
1322
|
+
return void 0;
|
|
1323
|
+
}
|
|
1324
|
+
return Number.isSafeInteger(parsed) ? parsed : void 0;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
943
1327
|
// src/orders/signer.ts
|
|
944
1328
|
var OrderSigner = class {
|
|
945
1329
|
/**
|
|
@@ -1603,9 +1987,14 @@ var OrderClient = class {
|
|
|
1603
1987
|
});
|
|
1604
1988
|
const portfolioFetcher = new PortfolioFetcher(this.httpClient);
|
|
1605
1989
|
const profile = await portfolioFetcher.getProfile(this.wallet.address);
|
|
1990
|
+
const userId = toFiniteInteger(profile.id);
|
|
1991
|
+
const feeRateBps = toFiniteInteger(profile.rank?.feeRateBps) ?? 300;
|
|
1992
|
+
if (userId === void 0) {
|
|
1993
|
+
throw new Error(`Invalid user profile id: ${profile.id}`);
|
|
1994
|
+
}
|
|
1606
1995
|
this.cachedUserData = {
|
|
1607
|
-
userId
|
|
1608
|
-
feeRateBps
|
|
1996
|
+
userId,
|
|
1997
|
+
feeRateBps
|
|
1609
1998
|
};
|
|
1610
1999
|
this.orderBuilder = new OrderBuilder(
|
|
1611
2000
|
this.wallet.address,
|
|
@@ -1692,6 +2081,7 @@ var OrderClient = class {
|
|
|
1692
2081
|
takerAmount: unsignedOrder.takerAmount
|
|
1693
2082
|
});
|
|
1694
2083
|
const signature = await this.orderSigner.signOrder(unsignedOrder, dynamicSigningConfig);
|
|
2084
|
+
const postOnly = params.orderType === "GTC" /* GTC */ && "postOnly" in params && params.postOnly !== void 0 ? params.postOnly : void 0;
|
|
1695
2085
|
const payload = {
|
|
1696
2086
|
order: {
|
|
1697
2087
|
...unsignedOrder,
|
|
@@ -1699,7 +2089,8 @@ var OrderClient = class {
|
|
|
1699
2089
|
},
|
|
1700
2090
|
orderType: params.orderType,
|
|
1701
2091
|
marketSlug: params.marketSlug,
|
|
1702
|
-
ownerId: userData.userId
|
|
2092
|
+
ownerId: userData.userId,
|
|
2093
|
+
...postOnly !== void 0 ? { postOnly } : {}
|
|
1703
2094
|
};
|
|
1704
2095
|
this.logger.debug("Submitting order to API", payload);
|
|
1705
2096
|
const apiResponse = await this.httpClient.post("/orders", payload);
|
|
@@ -1717,26 +2108,27 @@ var OrderClient = class {
|
|
|
1717
2108
|
* @internal
|
|
1718
2109
|
*/
|
|
1719
2110
|
transformOrderResponse(apiResponse) {
|
|
2111
|
+
const order = apiResponse.order;
|
|
1720
2112
|
const cleanOrder = {
|
|
1721
2113
|
order: {
|
|
1722
|
-
id:
|
|
1723
|
-
createdAt:
|
|
1724
|
-
makerAmount:
|
|
1725
|
-
takerAmount:
|
|
1726
|
-
expiration:
|
|
1727
|
-
signatureType:
|
|
1728
|
-
salt:
|
|
1729
|
-
maker:
|
|
1730
|
-
signer:
|
|
1731
|
-
taker:
|
|
1732
|
-
tokenId:
|
|
1733
|
-
side:
|
|
1734
|
-
feeRateBps:
|
|
1735
|
-
nonce:
|
|
1736
|
-
signature:
|
|
1737
|
-
orderType:
|
|
1738
|
-
price:
|
|
1739
|
-
marketId:
|
|
2114
|
+
id: order.id,
|
|
2115
|
+
createdAt: order.createdAt,
|
|
2116
|
+
makerAmount: toFiniteNumber(order.makerAmount) ?? order.makerAmount,
|
|
2117
|
+
takerAmount: toFiniteNumber(order.takerAmount) ?? order.takerAmount,
|
|
2118
|
+
expiration: order.expiration,
|
|
2119
|
+
signatureType: order.signatureType,
|
|
2120
|
+
salt: toFiniteInteger(order.salt) ?? order.salt,
|
|
2121
|
+
maker: order.maker,
|
|
2122
|
+
signer: order.signer,
|
|
2123
|
+
taker: order.taker,
|
|
2124
|
+
tokenId: order.tokenId,
|
|
2125
|
+
side: order.side,
|
|
2126
|
+
feeRateBps: order.feeRateBps,
|
|
2127
|
+
nonce: order.nonce,
|
|
2128
|
+
signature: order.signature,
|
|
2129
|
+
orderType: order.orderType,
|
|
2130
|
+
price: order.price === void 0 || order.price === null ? order.price : toFiniteNumber(order.price) ?? order.price,
|
|
2131
|
+
marketId: order.marketId
|
|
1740
2132
|
}
|
|
1741
2133
|
};
|
|
1742
2134
|
if (apiResponse.makerMatches && apiResponse.makerMatches.length > 0) {
|
|
@@ -2055,6 +2447,7 @@ var WebSocketClient = class {
|
|
|
2055
2447
|
this.config = {
|
|
2056
2448
|
url: config.url || DEFAULT_WS_URL,
|
|
2057
2449
|
apiKey: config.apiKey || process.env.LIMITLESS_API_KEY || "",
|
|
2450
|
+
hmacCredentials: config.hmacCredentials,
|
|
2058
2451
|
autoReconnect: config.autoReconnect ?? true,
|
|
2059
2452
|
reconnectDelay: config.reconnectDelay || 1e3,
|
|
2060
2453
|
maxReconnectAttempts: config.maxReconnectAttempts || Infinity,
|
|
@@ -2094,6 +2487,32 @@ var WebSocketClient = class {
|
|
|
2094
2487
|
this.reconnectWithNewAuth();
|
|
2095
2488
|
}
|
|
2096
2489
|
}
|
|
2490
|
+
/**
|
|
2491
|
+
* Sets HMAC credentials for authenticated subscriptions.
|
|
2492
|
+
*
|
|
2493
|
+
* @remarks
|
|
2494
|
+
* When configured alongside `apiKey`, this client uses HMAC headers for authenticated subscriptions.
|
|
2495
|
+
*/
|
|
2496
|
+
setHMACCredentials(hmacCredentials) {
|
|
2497
|
+
this.config.hmacCredentials = {
|
|
2498
|
+
tokenId: hmacCredentials.tokenId,
|
|
2499
|
+
secret: hmacCredentials.secret
|
|
2500
|
+
};
|
|
2501
|
+
if (this.socket?.connected) {
|
|
2502
|
+
this.logger.info("HMAC credentials updated, reconnecting...");
|
|
2503
|
+
this.reconnectWithNewAuth();
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
/**
|
|
2507
|
+
* Clears HMAC credentials.
|
|
2508
|
+
*/
|
|
2509
|
+
clearHMACCredentials() {
|
|
2510
|
+
this.config.hmacCredentials = void 0;
|
|
2511
|
+
if (this.socket?.connected) {
|
|
2512
|
+
this.logger.info("HMAC credentials cleared, reconnecting...");
|
|
2513
|
+
this.reconnectWithNewAuth();
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2097
2516
|
/**
|
|
2098
2517
|
* Reconnects with new authentication credentials.
|
|
2099
2518
|
* @internal
|
|
@@ -2139,10 +2558,29 @@ var WebSocketClient = class {
|
|
|
2139
2558
|
// Add jitter to prevent thundering herd
|
|
2140
2559
|
timeout: this.config.timeout
|
|
2141
2560
|
};
|
|
2142
|
-
|
|
2561
|
+
const extraHeaders = buildWebSocketTrackingHeaders();
|
|
2562
|
+
if (this.config.hmacCredentials) {
|
|
2563
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2564
|
+
const signature = computeHMACSignature(
|
|
2565
|
+
this.config.hmacCredentials.secret,
|
|
2566
|
+
timestamp,
|
|
2567
|
+
"GET",
|
|
2568
|
+
"/socket.io/?EIO=4&transport=websocket",
|
|
2569
|
+
""
|
|
2570
|
+
);
|
|
2143
2571
|
socketOptions.extraHeaders = {
|
|
2572
|
+
...extraHeaders,
|
|
2573
|
+
"lmts-api-key": this.config.hmacCredentials.tokenId,
|
|
2574
|
+
"lmts-timestamp": timestamp,
|
|
2575
|
+
"lmts-signature": signature
|
|
2576
|
+
};
|
|
2577
|
+
} else if (this.config.apiKey) {
|
|
2578
|
+
socketOptions.extraHeaders = {
|
|
2579
|
+
...extraHeaders,
|
|
2144
2580
|
"X-API-Key": this.config.apiKey
|
|
2145
2581
|
};
|
|
2582
|
+
} else if (Object.keys(extraHeaders).length > 0) {
|
|
2583
|
+
socketOptions.extraHeaders = extraHeaders;
|
|
2146
2584
|
}
|
|
2147
2585
|
this.socket = io(wsUrl + "/markets", socketOptions);
|
|
2148
2586
|
this.attachPendingListeners();
|
|
@@ -2211,9 +2649,9 @@ var WebSocketClient = class {
|
|
|
2211
2649
|
"subscribe_positions",
|
|
2212
2650
|
"subscribe_transactions"
|
|
2213
2651
|
];
|
|
2214
|
-
if (authenticatedChannels.includes(channel) && !this.config.apiKey) {
|
|
2652
|
+
if (authenticatedChannels.includes(channel) && !this.config.apiKey && !this.config.hmacCredentials) {
|
|
2215
2653
|
throw new Error(
|
|
2216
|
-
`
|
|
2654
|
+
`Authentication is required for '${channel}' subscription. Please provide either apiKey or hmacCredentials when creating the WebSocket client.`
|
|
2217
2655
|
);
|
|
2218
2656
|
}
|
|
2219
2657
|
const subscriptionKey = this.getSubscriptionKey(channel, options);
|
|
@@ -2407,15 +2845,74 @@ var WebSocketClient = class {
|
|
|
2407
2845
|
return key.split(":")[0];
|
|
2408
2846
|
}
|
|
2409
2847
|
};
|
|
2848
|
+
|
|
2849
|
+
// src/client.ts
|
|
2850
|
+
import { ethers as ethers3 } from "ethers";
|
|
2851
|
+
var Client = class _Client {
|
|
2852
|
+
constructor(config = {}) {
|
|
2853
|
+
this.http = new HttpClient(config);
|
|
2854
|
+
const logger = this.http.getLogger?.() || new NoOpLogger();
|
|
2855
|
+
this.markets = new MarketFetcher(this.http, logger);
|
|
2856
|
+
this.portfolio = new PortfolioFetcher(this.http, logger);
|
|
2857
|
+
this.pages = new MarketPageFetcher(this.http, logger);
|
|
2858
|
+
this.apiTokens = new ApiTokenService(this.http, logger);
|
|
2859
|
+
this.partnerAccounts = new PartnerAccountService(this.http, logger);
|
|
2860
|
+
this.delegatedOrders = new DelegatedOrderService(this.http, logger);
|
|
2861
|
+
}
|
|
2862
|
+
/**
|
|
2863
|
+
* Creates a root client around an existing shared HTTP client.
|
|
2864
|
+
*/
|
|
2865
|
+
static fromHttpClient(httpClient) {
|
|
2866
|
+
const client = Object.create(_Client.prototype);
|
|
2867
|
+
const logger = httpClient.getLogger?.() || new NoOpLogger();
|
|
2868
|
+
client.http = httpClient;
|
|
2869
|
+
client.markets = new MarketFetcher(httpClient, logger);
|
|
2870
|
+
client.portfolio = new PortfolioFetcher(httpClient, logger);
|
|
2871
|
+
client.pages = new MarketPageFetcher(httpClient, logger);
|
|
2872
|
+
client.apiTokens = new ApiTokenService(httpClient, logger);
|
|
2873
|
+
client.partnerAccounts = new PartnerAccountService(httpClient, logger);
|
|
2874
|
+
client.delegatedOrders = new DelegatedOrderService(httpClient, logger);
|
|
2875
|
+
return client;
|
|
2876
|
+
}
|
|
2877
|
+
/**
|
|
2878
|
+
* Creates a regular EIP-712 order client reusing the shared transport and market cache.
|
|
2879
|
+
*/
|
|
2880
|
+
newOrderClient(walletOrPrivateKey, config = {}) {
|
|
2881
|
+
const wallet = typeof walletOrPrivateKey === "string" ? new ethers3.Wallet(walletOrPrivateKey) : walletOrPrivateKey;
|
|
2882
|
+
return new OrderClient({
|
|
2883
|
+
httpClient: this.http,
|
|
2884
|
+
wallet,
|
|
2885
|
+
marketFetcher: this.markets,
|
|
2886
|
+
logger: this.http.getLogger(),
|
|
2887
|
+
...config
|
|
2888
|
+
});
|
|
2889
|
+
}
|
|
2890
|
+
/**
|
|
2891
|
+
* Creates a WebSocket client reusing shared auth where possible.
|
|
2892
|
+
*/
|
|
2893
|
+
newWebSocketClient(config = {}) {
|
|
2894
|
+
return new WebSocketClient(
|
|
2895
|
+
{
|
|
2896
|
+
apiKey: config.apiKey || this.http.getApiKey(),
|
|
2897
|
+
hmacCredentials: config.hmacCredentials || this.http.getHMACCredentials(),
|
|
2898
|
+
...config
|
|
2899
|
+
},
|
|
2900
|
+
this.http.getLogger()
|
|
2901
|
+
);
|
|
2902
|
+
}
|
|
2903
|
+
};
|
|
2410
2904
|
export {
|
|
2411
2905
|
APIError,
|
|
2906
|
+
ApiTokenService,
|
|
2412
2907
|
AuthenticationError,
|
|
2413
2908
|
BASE_SEPOLIA_CHAIN_ID,
|
|
2414
2909
|
CONTRACT_ADDRESSES,
|
|
2910
|
+
Client,
|
|
2415
2911
|
ConsoleLogger,
|
|
2416
2912
|
DEFAULT_API_URL,
|
|
2417
2913
|
DEFAULT_CHAIN_ID,
|
|
2418
2914
|
DEFAULT_WS_URL,
|
|
2915
|
+
DelegatedOrderService,
|
|
2419
2916
|
HttpClient,
|
|
2420
2917
|
Market,
|
|
2421
2918
|
MarketFetcher,
|
|
@@ -2426,19 +2923,27 @@ export {
|
|
|
2426
2923
|
OrderSigner,
|
|
2427
2924
|
OrderType,
|
|
2428
2925
|
OrderValidationError,
|
|
2926
|
+
PartnerAccountService,
|
|
2429
2927
|
PortfolioFetcher,
|
|
2430
2928
|
RateLimitError,
|
|
2431
2929
|
RetryConfig,
|
|
2432
2930
|
RetryableClient,
|
|
2433
2931
|
SIGNING_MESSAGE_TEMPLATE,
|
|
2932
|
+
ScopeAccountCreation,
|
|
2933
|
+
ScopeDelegatedSigning,
|
|
2934
|
+
ScopeTrading,
|
|
2434
2935
|
Side,
|
|
2435
2936
|
SignatureType,
|
|
2436
2937
|
ValidationError,
|
|
2437
2938
|
WebSocketClient,
|
|
2438
2939
|
WebSocketState,
|
|
2439
2940
|
ZERO_ADDRESS,
|
|
2941
|
+
buildHMACMessage,
|
|
2942
|
+
computeHMACSignature,
|
|
2440
2943
|
getContractAddress,
|
|
2441
2944
|
retryOnErrors,
|
|
2945
|
+
toFiniteInteger,
|
|
2946
|
+
toFiniteNumber,
|
|
2442
2947
|
validateOrderArgs,
|
|
2443
2948
|
validateSignedOrder,
|
|
2444
2949
|
validateUnsignedOrder,
|