@limitless-exchange/sdk 1.0.2 → 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 +78 -38
- package/dist/index.d.mts +728 -54
- package/dist/index.d.ts +728 -54
- package/dist/index.js +773 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +761 -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() {
|
|
@@ -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
|
);
|
|
@@ -352,6 +430,42 @@ var HttpClient = class {
|
|
|
352
430
|
}
|
|
353
431
|
);
|
|
354
432
|
}
|
|
433
|
+
/**
|
|
434
|
+
* Extracts a human-readable error message from API response payload.
|
|
435
|
+
* @internal
|
|
436
|
+
*/
|
|
437
|
+
extractErrorMessage(data, fallback) {
|
|
438
|
+
if (!data) {
|
|
439
|
+
return fallback;
|
|
440
|
+
}
|
|
441
|
+
if (typeof data === "object") {
|
|
442
|
+
if (Array.isArray(data.message)) {
|
|
443
|
+
const messages = data.message.map((err) => {
|
|
444
|
+
const details = Object.entries(err || {}).filter(([_key, val]) => val !== "" && val !== null && val !== void 0).map(([key, val]) => `${key}: ${val}`).join(", ");
|
|
445
|
+
return details || JSON.stringify(err);
|
|
446
|
+
}).filter((msg) => msg.trim() !== "").join(" | ");
|
|
447
|
+
return messages || data.error || JSON.stringify(data);
|
|
448
|
+
}
|
|
449
|
+
return data.message || data.error || data.msg || data.errors && JSON.stringify(data.errors) || JSON.stringify(data);
|
|
450
|
+
}
|
|
451
|
+
return String(data);
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Creates a typed API error class from status code.
|
|
455
|
+
* @internal
|
|
456
|
+
*/
|
|
457
|
+
createTypedApiError(status, message, data, url, method) {
|
|
458
|
+
if (status === 429) {
|
|
459
|
+
return new RateLimitError(message, status, data, url, method);
|
|
460
|
+
}
|
|
461
|
+
if (status === 401 || status === 403) {
|
|
462
|
+
return new AuthenticationError(message, status, data, url, method);
|
|
463
|
+
}
|
|
464
|
+
if (status === 400) {
|
|
465
|
+
return new ValidationError(message, status, data, url, method);
|
|
466
|
+
}
|
|
467
|
+
return new APIError(message, status, data, url, method);
|
|
468
|
+
}
|
|
355
469
|
/**
|
|
356
470
|
* Sets the API key for authenticated requests.
|
|
357
471
|
*
|
|
@@ -360,12 +474,86 @@ var HttpClient = class {
|
|
|
360
474
|
setApiKey(apiKey) {
|
|
361
475
|
this.apiKey = apiKey;
|
|
362
476
|
}
|
|
477
|
+
/**
|
|
478
|
+
* Returns the configured API key, if any.
|
|
479
|
+
*/
|
|
480
|
+
getApiKey() {
|
|
481
|
+
return this.apiKey;
|
|
482
|
+
}
|
|
363
483
|
/**
|
|
364
484
|
* Clears the API key.
|
|
365
485
|
*/
|
|
366
486
|
clearApiKey() {
|
|
367
487
|
this.apiKey = void 0;
|
|
368
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
|
+
}
|
|
369
557
|
/**
|
|
370
558
|
* Performs a GET request.
|
|
371
559
|
*
|
|
@@ -377,6 +565,38 @@ var HttpClient = class {
|
|
|
377
565
|
const response = await this.client.get(url, config);
|
|
378
566
|
return response.data;
|
|
379
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
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Performs a GET request and returns raw response metadata.
|
|
580
|
+
*
|
|
581
|
+
* @remarks
|
|
582
|
+
* Use this when callers need access to status code or headers (e.g. redirect `Location`).
|
|
583
|
+
*
|
|
584
|
+
* @param url - Request URL
|
|
585
|
+
* @param config - Additional request configuration
|
|
586
|
+
* @returns Promise resolving to status, headers, and response data
|
|
587
|
+
*/
|
|
588
|
+
async getRaw(url, config) {
|
|
589
|
+
const response = await this.client.get(url, config);
|
|
590
|
+
if (response.status >= 400) {
|
|
591
|
+
const message = this.extractErrorMessage(response.data, `Request failed with status ${response.status}`);
|
|
592
|
+
throw this.createTypedApiError(response.status, message, response.data, url, "GET");
|
|
593
|
+
}
|
|
594
|
+
return {
|
|
595
|
+
status: response.status,
|
|
596
|
+
headers: response.headers,
|
|
597
|
+
data: response.data
|
|
598
|
+
};
|
|
599
|
+
}
|
|
380
600
|
/**
|
|
381
601
|
* Performs a POST request.
|
|
382
602
|
*
|
|
@@ -389,6 +609,36 @@ var HttpClient = class {
|
|
|
389
609
|
const response = await this.client.post(url, data, config);
|
|
390
610
|
return response.data;
|
|
391
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
|
+
}
|
|
392
642
|
/**
|
|
393
643
|
* Performs a DELETE request.
|
|
394
644
|
*
|
|
@@ -411,6 +661,28 @@ var HttpClient = class {
|
|
|
411
661
|
const response = await this.client.delete(url, deleteConfig);
|
|
412
662
|
return response.data;
|
|
413
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
|
+
}
|
|
414
686
|
};
|
|
415
687
|
|
|
416
688
|
// src/api/retry.ts
|
|
@@ -594,6 +866,75 @@ var RetryableClient = class {
|
|
|
594
866
|
}
|
|
595
867
|
};
|
|
596
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
|
+
|
|
597
938
|
// src/orders/builder.ts
|
|
598
939
|
import { ethers } from "ethers";
|
|
599
940
|
var ZERO_ADDRESS2 = "0x0000000000000000000000000000000000000000";
|
|
@@ -882,6 +1223,104 @@ var OrderBuilder = class {
|
|
|
882
1223
|
}
|
|
883
1224
|
};
|
|
884
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
|
+
|
|
885
1324
|
// src/orders/signer.ts
|
|
886
1325
|
var OrderSigner = class {
|
|
887
1326
|
/**
|
|
@@ -1023,7 +1462,7 @@ var OrderValidationError = class extends Error {
|
|
|
1023
1462
|
}
|
|
1024
1463
|
};
|
|
1025
1464
|
function isFOKOrder(args) {
|
|
1026
|
-
return "
|
|
1465
|
+
return "makerAmount" in args;
|
|
1027
1466
|
}
|
|
1028
1467
|
function validateOrderArgs(args) {
|
|
1029
1468
|
if (!args.tokenId) {
|
|
@@ -1545,9 +1984,14 @@ var OrderClient = class {
|
|
|
1545
1984
|
});
|
|
1546
1985
|
const portfolioFetcher = new PortfolioFetcher(this.httpClient);
|
|
1547
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
|
+
}
|
|
1548
1992
|
this.cachedUserData = {
|
|
1549
|
-
userId
|
|
1550
|
-
feeRateBps
|
|
1993
|
+
userId,
|
|
1994
|
+
feeRateBps
|
|
1551
1995
|
};
|
|
1552
1996
|
this.orderBuilder = new OrderBuilder(
|
|
1553
1997
|
this.wallet.address,
|
|
@@ -1659,26 +2103,27 @@ var OrderClient = class {
|
|
|
1659
2103
|
* @internal
|
|
1660
2104
|
*/
|
|
1661
2105
|
transformOrderResponse(apiResponse) {
|
|
2106
|
+
const order = apiResponse.order;
|
|
1662
2107
|
const cleanOrder = {
|
|
1663
2108
|
order: {
|
|
1664
|
-
id:
|
|
1665
|
-
createdAt:
|
|
1666
|
-
makerAmount:
|
|
1667
|
-
takerAmount:
|
|
1668
|
-
expiration:
|
|
1669
|
-
signatureType:
|
|
1670
|
-
salt:
|
|
1671
|
-
maker:
|
|
1672
|
-
signer:
|
|
1673
|
-
taker:
|
|
1674
|
-
tokenId:
|
|
1675
|
-
side:
|
|
1676
|
-
feeRateBps:
|
|
1677
|
-
nonce:
|
|
1678
|
-
signature:
|
|
1679
|
-
orderType:
|
|
1680
|
-
price:
|
|
1681
|
-
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
|
|
1682
2127
|
}
|
|
1683
2128
|
};
|
|
1684
2129
|
if (apiResponse.makerMatches && apiResponse.makerMatches.length > 0) {
|
|
@@ -1811,6 +2256,174 @@ var OrderClient = class {
|
|
|
1811
2256
|
}
|
|
1812
2257
|
};
|
|
1813
2258
|
|
|
2259
|
+
// src/market-pages/fetcher.ts
|
|
2260
|
+
var MAX_REDIRECT_DEPTH = 3;
|
|
2261
|
+
var MarketPageFetcher = class {
|
|
2262
|
+
/**
|
|
2263
|
+
* Creates a new market-pages fetcher.
|
|
2264
|
+
*
|
|
2265
|
+
* @param httpClient - HTTP client for API calls
|
|
2266
|
+
* @param logger - Optional logger
|
|
2267
|
+
*/
|
|
2268
|
+
constructor(httpClient, logger) {
|
|
2269
|
+
this.httpClient = httpClient;
|
|
2270
|
+
this.logger = logger || new NoOpLogger();
|
|
2271
|
+
}
|
|
2272
|
+
/**
|
|
2273
|
+
* Gets the navigation tree.
|
|
2274
|
+
*/
|
|
2275
|
+
async getNavigation() {
|
|
2276
|
+
this.logger.debug("Fetching navigation tree");
|
|
2277
|
+
return this.httpClient.get("/navigation");
|
|
2278
|
+
}
|
|
2279
|
+
/**
|
|
2280
|
+
* Resolves a market page by path.
|
|
2281
|
+
*
|
|
2282
|
+
* @remarks
|
|
2283
|
+
* Handles 301 redirects manually by re-requesting `/market-pages/by-path` with the
|
|
2284
|
+
* redirected path value from `Location` header.
|
|
2285
|
+
*/
|
|
2286
|
+
async getMarketPageByPath(path) {
|
|
2287
|
+
return this.getMarketPageByPathInternal(path, 0);
|
|
2288
|
+
}
|
|
2289
|
+
async getMarketPageByPathInternal(path, depth) {
|
|
2290
|
+
const query = new URLSearchParams({ path }).toString();
|
|
2291
|
+
const endpoint = `/market-pages/by-path?${query}`;
|
|
2292
|
+
const requestConfig = {
|
|
2293
|
+
maxRedirects: 0,
|
|
2294
|
+
validateStatus: (status) => status === 200 || status === 301
|
|
2295
|
+
};
|
|
2296
|
+
this.logger.debug("Resolving market page by path", { path, depth });
|
|
2297
|
+
const response = await this.httpClient.getRaw(endpoint, requestConfig);
|
|
2298
|
+
if (response.status === 200) {
|
|
2299
|
+
return response.data;
|
|
2300
|
+
}
|
|
2301
|
+
if (response.status !== 301) {
|
|
2302
|
+
throw new Error(`Unexpected response status: ${response.status}`);
|
|
2303
|
+
}
|
|
2304
|
+
if (depth >= MAX_REDIRECT_DEPTH) {
|
|
2305
|
+
throw new Error(
|
|
2306
|
+
`Too many redirects while resolving market page path '${path}' (max ${MAX_REDIRECT_DEPTH})`
|
|
2307
|
+
);
|
|
2308
|
+
}
|
|
2309
|
+
const locationHeader = response.headers?.location;
|
|
2310
|
+
const location = Array.isArray(locationHeader) ? locationHeader[0] : locationHeader;
|
|
2311
|
+
if (!location || typeof location !== "string") {
|
|
2312
|
+
throw new Error("Redirect response missing valid Location header");
|
|
2313
|
+
}
|
|
2314
|
+
const redirectedPath = this.extractRedirectPath(location);
|
|
2315
|
+
this.logger.info("Following market page redirect", {
|
|
2316
|
+
from: path,
|
|
2317
|
+
to: redirectedPath,
|
|
2318
|
+
depth: depth + 1
|
|
2319
|
+
});
|
|
2320
|
+
return this.getMarketPageByPathInternal(redirectedPath, depth + 1);
|
|
2321
|
+
}
|
|
2322
|
+
extractRedirectPath(location) {
|
|
2323
|
+
const directByPathPrefix = "/market-pages/by-path";
|
|
2324
|
+
if (location.startsWith(directByPathPrefix)) {
|
|
2325
|
+
const url = new URL(location, "https://api.limitless.exchange");
|
|
2326
|
+
const path = url.searchParams.get("path");
|
|
2327
|
+
if (!path) {
|
|
2328
|
+
throw new Error("Redirect location '/market-pages/by-path' is missing required 'path' query parameter");
|
|
2329
|
+
}
|
|
2330
|
+
return path;
|
|
2331
|
+
}
|
|
2332
|
+
if (/^https?:\/\//i.test(location)) {
|
|
2333
|
+
const url = new URL(location);
|
|
2334
|
+
if (url.pathname === directByPathPrefix) {
|
|
2335
|
+
const path = url.searchParams.get("path");
|
|
2336
|
+
if (!path) {
|
|
2337
|
+
throw new Error("Redirect location '/market-pages/by-path' is missing required 'path' query parameter");
|
|
2338
|
+
}
|
|
2339
|
+
return path;
|
|
2340
|
+
}
|
|
2341
|
+
return url.pathname || "/";
|
|
2342
|
+
}
|
|
2343
|
+
return location;
|
|
2344
|
+
}
|
|
2345
|
+
/**
|
|
2346
|
+
* Gets markets for a market page with optional filtering and pagination.
|
|
2347
|
+
*/
|
|
2348
|
+
async getMarkets(pageId, params = {}) {
|
|
2349
|
+
if (params.cursor !== void 0 && params.page !== void 0) {
|
|
2350
|
+
throw new Error("Parameters `cursor` and `page` are mutually exclusive");
|
|
2351
|
+
}
|
|
2352
|
+
const query = new URLSearchParams();
|
|
2353
|
+
if (params.page !== void 0) {
|
|
2354
|
+
query.append("page", String(params.page));
|
|
2355
|
+
}
|
|
2356
|
+
if (params.limit !== void 0) {
|
|
2357
|
+
query.append("limit", String(params.limit));
|
|
2358
|
+
}
|
|
2359
|
+
if (params.sort) {
|
|
2360
|
+
query.append("sort", params.sort);
|
|
2361
|
+
}
|
|
2362
|
+
if (params.cursor !== void 0) {
|
|
2363
|
+
query.append("cursor", params.cursor);
|
|
2364
|
+
}
|
|
2365
|
+
if (params.filters) {
|
|
2366
|
+
for (const [key, value] of Object.entries(params.filters)) {
|
|
2367
|
+
if (Array.isArray(value)) {
|
|
2368
|
+
for (const item of value) {
|
|
2369
|
+
query.append(key, this.stringifyFilterValue(item));
|
|
2370
|
+
}
|
|
2371
|
+
} else {
|
|
2372
|
+
query.append(key, this.stringifyFilterValue(value));
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
const queryString = query.toString();
|
|
2377
|
+
const endpoint = `/market-pages/${pageId}/markets${queryString ? `?${queryString}` : ""}`;
|
|
2378
|
+
this.logger.debug("Fetching market-page markets", { pageId, params });
|
|
2379
|
+
const response = await this.httpClient.get(endpoint);
|
|
2380
|
+
const markets = (response.data || []).map((marketData) => new Market(marketData, this.httpClient));
|
|
2381
|
+
if (response.pagination) {
|
|
2382
|
+
return {
|
|
2383
|
+
data: markets,
|
|
2384
|
+
pagination: response.pagination
|
|
2385
|
+
};
|
|
2386
|
+
}
|
|
2387
|
+
if (response.cursor) {
|
|
2388
|
+
return {
|
|
2389
|
+
data: markets,
|
|
2390
|
+
cursor: response.cursor
|
|
2391
|
+
};
|
|
2392
|
+
}
|
|
2393
|
+
throw new Error("Invalid market-page response: expected `pagination` or `cursor` metadata");
|
|
2394
|
+
}
|
|
2395
|
+
/**
|
|
2396
|
+
* Lists all property keys with options.
|
|
2397
|
+
*/
|
|
2398
|
+
async getPropertyKeys() {
|
|
2399
|
+
return this.httpClient.get("/property-keys");
|
|
2400
|
+
}
|
|
2401
|
+
/**
|
|
2402
|
+
* Gets a single property key by ID.
|
|
2403
|
+
*/
|
|
2404
|
+
async getPropertyKey(id) {
|
|
2405
|
+
return this.httpClient.get(`/property-keys/${id}`);
|
|
2406
|
+
}
|
|
2407
|
+
/**
|
|
2408
|
+
* Lists options for a property key, optionally filtered by parent option ID.
|
|
2409
|
+
*/
|
|
2410
|
+
async getPropertyOptions(keyId, parentId) {
|
|
2411
|
+
const query = new URLSearchParams();
|
|
2412
|
+
if (parentId) {
|
|
2413
|
+
query.append("parentId", parentId);
|
|
2414
|
+
}
|
|
2415
|
+
const queryString = query.toString();
|
|
2416
|
+
const endpoint = `/property-keys/${keyId}/options${queryString ? `?${queryString}` : ""}`;
|
|
2417
|
+
return this.httpClient.get(endpoint);
|
|
2418
|
+
}
|
|
2419
|
+
stringifyFilterValue(value) {
|
|
2420
|
+
if (typeof value === "boolean") {
|
|
2421
|
+
return value ? "true" : "false";
|
|
2422
|
+
}
|
|
2423
|
+
return String(value);
|
|
2424
|
+
}
|
|
2425
|
+
};
|
|
2426
|
+
|
|
1814
2427
|
// src/websocket/client.ts
|
|
1815
2428
|
import { io } from "socket.io-client";
|
|
1816
2429
|
var WebSocketClient = class {
|
|
@@ -1829,6 +2442,7 @@ var WebSocketClient = class {
|
|
|
1829
2442
|
this.config = {
|
|
1830
2443
|
url: config.url || DEFAULT_WS_URL,
|
|
1831
2444
|
apiKey: config.apiKey || process.env.LIMITLESS_API_KEY || "",
|
|
2445
|
+
hmacCredentials: config.hmacCredentials,
|
|
1832
2446
|
autoReconnect: config.autoReconnect ?? true,
|
|
1833
2447
|
reconnectDelay: config.reconnectDelay || 1e3,
|
|
1834
2448
|
maxReconnectAttempts: config.maxReconnectAttempts || Infinity,
|
|
@@ -1868,6 +2482,32 @@ var WebSocketClient = class {
|
|
|
1868
2482
|
this.reconnectWithNewAuth();
|
|
1869
2483
|
}
|
|
1870
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
|
+
}
|
|
1871
2511
|
/**
|
|
1872
2512
|
* Reconnects with new authentication credentials.
|
|
1873
2513
|
* @internal
|
|
@@ -1913,10 +2553,29 @@ var WebSocketClient = class {
|
|
|
1913
2553
|
// Add jitter to prevent thundering herd
|
|
1914
2554
|
timeout: this.config.timeout
|
|
1915
2555
|
};
|
|
1916
|
-
|
|
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
|
+
);
|
|
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) {
|
|
1917
2573
|
socketOptions.extraHeaders = {
|
|
2574
|
+
...extraHeaders,
|
|
1918
2575
|
"X-API-Key": this.config.apiKey
|
|
1919
2576
|
};
|
|
2577
|
+
} else if (Object.keys(extraHeaders).length > 0) {
|
|
2578
|
+
socketOptions.extraHeaders = extraHeaders;
|
|
1920
2579
|
}
|
|
1921
2580
|
this.socket = io(wsUrl + "/markets", socketOptions);
|
|
1922
2581
|
this.attachPendingListeners();
|
|
@@ -1985,9 +2644,9 @@ var WebSocketClient = class {
|
|
|
1985
2644
|
"subscribe_positions",
|
|
1986
2645
|
"subscribe_transactions"
|
|
1987
2646
|
];
|
|
1988
|
-
if (authenticatedChannels.includes(channel) && !this.config.apiKey) {
|
|
2647
|
+
if (authenticatedChannels.includes(channel) && !this.config.apiKey && !this.config.hmacCredentials) {
|
|
1989
2648
|
throw new Error(
|
|
1990
|
-
`
|
|
2649
|
+
`Authentication is required for '${channel}' subscription. Please provide either apiKey or hmacCredentials when creating the WebSocket client.`
|
|
1991
2650
|
);
|
|
1992
2651
|
}
|
|
1993
2652
|
const subscriptionKey = this.getSubscriptionKey(channel, options);
|
|
@@ -2181,37 +2840,105 @@ var WebSocketClient = class {
|
|
|
2181
2840
|
return key.split(":")[0];
|
|
2182
2841
|
}
|
|
2183
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
|
+
};
|
|
2184
2899
|
export {
|
|
2185
2900
|
APIError,
|
|
2901
|
+
ApiTokenService,
|
|
2186
2902
|
AuthenticationError,
|
|
2187
2903
|
BASE_SEPOLIA_CHAIN_ID,
|
|
2188
2904
|
CONTRACT_ADDRESSES,
|
|
2905
|
+
Client,
|
|
2189
2906
|
ConsoleLogger,
|
|
2190
2907
|
DEFAULT_API_URL,
|
|
2191
2908
|
DEFAULT_CHAIN_ID,
|
|
2192
2909
|
DEFAULT_WS_URL,
|
|
2910
|
+
DelegatedOrderService,
|
|
2193
2911
|
HttpClient,
|
|
2194
2912
|
Market,
|
|
2195
2913
|
MarketFetcher,
|
|
2914
|
+
MarketPageFetcher,
|
|
2196
2915
|
NoOpLogger,
|
|
2197
2916
|
OrderBuilder,
|
|
2198
2917
|
OrderClient,
|
|
2199
2918
|
OrderSigner,
|
|
2200
2919
|
OrderType,
|
|
2201
2920
|
OrderValidationError,
|
|
2921
|
+
PartnerAccountService,
|
|
2202
2922
|
PortfolioFetcher,
|
|
2203
2923
|
RateLimitError,
|
|
2204
2924
|
RetryConfig,
|
|
2205
2925
|
RetryableClient,
|
|
2206
2926
|
SIGNING_MESSAGE_TEMPLATE,
|
|
2927
|
+
ScopeAccountCreation,
|
|
2928
|
+
ScopeDelegatedSigning,
|
|
2929
|
+
ScopeTrading,
|
|
2207
2930
|
Side,
|
|
2208
2931
|
SignatureType,
|
|
2209
2932
|
ValidationError,
|
|
2210
2933
|
WebSocketClient,
|
|
2211
2934
|
WebSocketState,
|
|
2212
2935
|
ZERO_ADDRESS,
|
|
2936
|
+
buildHMACMessage,
|
|
2937
|
+
computeHMACSignature,
|
|
2213
2938
|
getContractAddress,
|
|
2214
2939
|
retryOnErrors,
|
|
2940
|
+
toFiniteInteger,
|
|
2941
|
+
toFiniteNumber,
|
|
2215
2942
|
validateOrderArgs,
|
|
2216
2943
|
validateSignedOrder,
|
|
2217
2944
|
validateUnsignedOrder,
|