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