pmxtjs 2.51.1 → 2.51.3
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 +14 -5
- package/dist/esm/generated/src/apis/DefaultApi.d.ts +6 -0
- package/dist/esm/generated/src/apis/DefaultApi.js +6 -0
- package/dist/esm/generated/src/models/CreateOrderParams.d.ts +20 -0
- package/dist/esm/generated/src/models/CreateOrderParams.js +11 -0
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +3 -2
- package/dist/esm/pmxt/client.d.ts +61 -12
- package/dist/esm/pmxt/client.js +126 -21
- package/dist/esm/pmxt/escrow.js +109 -10
- package/dist/esm/pmxt/hosted-typed-data.d.ts +1 -1
- package/dist/esm/pmxt/hosted-typed-data.js +14 -0
- package/dist/esm/pmxt/models.d.ts +17 -0
- package/dist/esm/pmxt/server-manager.js +5 -3
- package/dist/generated/src/apis/DefaultApi.d.ts +6 -0
- package/dist/generated/src/apis/DefaultApi.js +6 -0
- package/dist/generated/src/models/CreateOrderParams.d.ts +20 -0
- package/dist/generated/src/models/CreateOrderParams.js +12 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -1
- package/dist/pmxt/client.d.ts +61 -12
- package/dist/pmxt/client.js +126 -20
- package/dist/pmxt/escrow.js +109 -10
- package/dist/pmxt/hosted-typed-data.d.ts +1 -1
- package/dist/pmxt/hosted-typed-data.js +14 -0
- package/dist/pmxt/models.d.ts +17 -0
- package/dist/pmxt/server-manager.js +5 -3
- package/generated/docs/ArbitrageOpportunity.md +0 -29
- package/generated/docs/Balance.md +0 -25
- package/generated/docs/BaseRequest.md +0 -21
- package/generated/docs/BaseResponse.md +0 -22
- package/generated/docs/BuildOrder200Response.md +0 -23
- package/generated/docs/BuildOrderRequest.md +0 -22
- package/generated/docs/BuiltOrder.md +0 -26
- package/generated/docs/BuiltOrderTx.md +0 -24
- package/generated/docs/CancelOrderRequest.md +0 -22
- package/generated/docs/CloseRequest.md +0 -22
- package/generated/docs/CompareMarketPrices200Response.md +0 -23
- package/generated/docs/CompareMarketPricesRequest.md +0 -22
- package/generated/docs/CreateOrder200Response.md +0 -23
- package/generated/docs/CreateOrderParams.md +2 -30
- package/generated/docs/CreateOrderRequest.md +0 -22
- package/generated/docs/DataFeedsApi.md +0 -329
- package/generated/docs/DefaultApi.md +3 -1438
- package/generated/docs/ErrorDetail.md +0 -25
- package/generated/docs/ErrorResponse.md +0 -22
- package/generated/docs/EventFetchParams.md +0 -35
- package/generated/docs/EventFilterCriteria.md +0 -26
- package/generated/docs/EventFilterCriteriaTotalVolume.md +0 -22
- package/generated/docs/EventMatchResult.md +0 -22
- package/generated/docs/ExchangeCredentials.md +0 -29
- package/generated/docs/ExchangeCredentialsSignatureType.md +0 -20
- package/generated/docs/ExchangeOptions.md +0 -27
- package/generated/docs/ExecutionPriceResult.md +0 -23
- package/generated/docs/FeedFetchHistoricalPrices200Response.md +0 -22
- package/generated/docs/FeedFetchOHLCV200Response.md +0 -22
- package/generated/docs/FeedFetchOracleHistory200Response.md +0 -22
- package/generated/docs/FeedFetchOracleRound200Response.md +0 -22
- package/generated/docs/FeedFetchOrderBook200Response.md +0 -22
- package/generated/docs/FeedFetchOrderBook200ResponseData.md +0 -25
- package/generated/docs/FeedFetchTicker200Response.md +0 -22
- package/generated/docs/FeedFetchTickers200Response.md +0 -22
- package/generated/docs/FeedList200Response.md +0 -22
- package/generated/docs/FeedLoadMarkets200Response.md +0 -22
- package/generated/docs/FeedMarket.md +0 -27
- package/generated/docs/FeedOracleRound.md +0 -28
- package/generated/docs/FeedTicker.md +0 -42
- package/generated/docs/FetchArbitrage200Response.md +0 -23
- package/generated/docs/FetchArbitrageParams.md +0 -24
- package/generated/docs/FetchBalance200Response.md +0 -23
- package/generated/docs/FetchEvent200Response.md +0 -23
- package/generated/docs/FetchEventMatches200Response.md +0 -23
- package/generated/docs/FetchEventMatchesParams.md +0 -29
- package/generated/docs/FetchEvents200Response.md +0 -23
- package/generated/docs/FetchEventsPaginated200Response.md +0 -23
- package/generated/docs/FetchMarket200Response.md +0 -23
- package/generated/docs/FetchMarketMatches200Response.md +0 -23
- package/generated/docs/FetchMarketMatchesParams.md +0 -32
- package/generated/docs/FetchMarkets200Response.md +0 -23
- package/generated/docs/FetchMarketsPaginated200Response.md +0 -23
- package/generated/docs/FetchMatchedMarkets200Response.md +0 -23
- package/generated/docs/FetchMatchedMarketsParams.md +0 -24
- package/generated/docs/FetchMyTrades200Response.md +0 -23
- package/generated/docs/FetchOHLCV200Response.md +0 -23
- package/generated/docs/FetchOpenOrders200Response.md +0 -23
- package/generated/docs/FetchOrderBook200Response.md +0 -23
- package/generated/docs/FetchOrderBookParams.md +0 -24
- package/generated/docs/FetchOrderBooks200Response.md +0 -23
- package/generated/docs/FetchOrderBooksRequest.md +0 -22
- package/generated/docs/FetchPositions200Response.md +0 -23
- package/generated/docs/FetchSeries200Response.md +0 -23
- package/generated/docs/FetchTrades200Response.md +0 -23
- package/generated/docs/FilterEventsRequest.md +0 -22
- package/generated/docs/FilterEventsRequestArgsInner.md +0 -26
- package/generated/docs/FilterMarketsRequest.md +0 -22
- package/generated/docs/FilterMarketsRequestArgsInner.md +0 -31
- package/generated/docs/GetExecutionPrice200Response.md +0 -23
- package/generated/docs/GetExecutionPriceDetailed200Response.md +0 -23
- package/generated/docs/GetExecutionPriceDetailedRequest.md +0 -22
- package/generated/docs/GetExecutionPriceRequest.md +0 -22
- package/generated/docs/GetExecutionPriceRequestArgsInner.md +0 -27
- package/generated/docs/HealthCheck200Response.md +0 -22
- package/generated/docs/HistoryFilterParams.md +0 -24
- package/generated/docs/LoadMarkets200Response.md +0 -23
- package/generated/docs/LoadMarketsRequest.md +0 -22
- package/generated/docs/MarketFilterCriteria.md +0 -31
- package/generated/docs/MarketFilterCriteriaLiquidity.md +0 -22
- package/generated/docs/MarketFilterCriteriaOpenInterest.md +0 -22
- package/generated/docs/MarketFilterCriteriaPrice.md +0 -23
- package/generated/docs/MarketFilterCriteriaResolutionDate.md +0 -22
- package/generated/docs/MarketFilterCriteriaVolume.md +0 -22
- package/generated/docs/MarketFilterCriteriaVolume24h.md +0 -22
- package/generated/docs/MarketFilterParams.md +0 -34
- package/generated/docs/MarketOutcome.md +0 -26
- package/generated/docs/MatchResult.md +0 -27
- package/generated/docs/MatchedMarketPair.md +0 -30
- package/generated/docs/MyTradesParams.md +0 -26
- package/generated/docs/OHLCVParams.md +0 -24
- package/generated/docs/Order.md +0 -37
- package/generated/docs/OrderBook.md +0 -27
- package/generated/docs/OrderHistoryParams.md +0 -25
- package/generated/docs/OrderLevel.md +0 -23
- package/generated/docs/PaginatedEventsResult.md +0 -23
- package/generated/docs/PaginatedMarketsResult.md +0 -23
- package/generated/docs/Position.md +0 -32
- package/generated/docs/PriceCandle.md +0 -26
- package/generated/docs/PriceComparison.md +0 -27
- package/generated/docs/SubmitOrderRequest.md +0 -22
- package/generated/docs/Trade.md +0 -26
- package/generated/docs/TradesParams.md +0 -23
- package/generated/docs/UnifiedEvent.md +0 -33
- package/generated/docs/UnifiedMarket.md +0 -44
- package/generated/docs/UnifiedSeries.md +0 -31
- package/generated/docs/UserTrade.md +0 -32
- package/generated/package.json +1 -1
- package/generated/src/apis/DefaultApi.ts +6 -0
- package/generated/src/models/CreateOrderParams.ts +25 -0
- package/index.ts +3 -2
- package/package.json +2 -2
- package/pmxt/client.ts +153 -28
- package/pmxt/escrow.ts +131 -11
- package/pmxt/hosted-typed-data.ts +17 -1
- package/pmxt/models.ts +20 -0
- package/pmxt/server-manager.ts +5 -3
package/pmxt/client.ts
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
import {
|
|
18
18
|
Balance,
|
|
19
19
|
BuiltOrder,
|
|
20
|
-
|
|
20
|
+
CreateOrderInput,
|
|
21
21
|
EventFetchParams,
|
|
22
22
|
EventFilterCriteria,
|
|
23
23
|
EventFilterFunction,
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
import { ServerManager } from "./server-manager.js";
|
|
53
53
|
import { buildArgsWithOptionalOptions } from "./args.js";
|
|
54
54
|
import { PmxtError, fromServerError, InvalidOrder, NotSupported } from "./errors.js";
|
|
55
|
-
import { LOCAL_URL, resolvePmxtBaseUrl } from "./constants.js";
|
|
55
|
+
import { ENV, LOCAL_URL, resolvePmxtBaseUrl } from "./constants.js";
|
|
56
56
|
import { SidecarWsClient } from "./ws-client.js";
|
|
57
57
|
import { logger } from "./logger.js";
|
|
58
58
|
|
|
@@ -82,6 +82,7 @@ import {
|
|
|
82
82
|
validateEconomics,
|
|
83
83
|
verifySignature,
|
|
84
84
|
} from "./hosted-typed-data.js";
|
|
85
|
+
import type { HostedRoute } from "./hosted-typed-data.js";
|
|
85
86
|
import type { Signer, TypedData } from "./signers.js";
|
|
86
87
|
import { signerFromPrivateKey, EthersSigner } from "./signers.js";
|
|
87
88
|
import { Escrow } from "./escrow.js";
|
|
@@ -351,6 +352,7 @@ export abstract class Exchange {
|
|
|
351
352
|
private _wsClient: SidecarWsClient | null = null;
|
|
352
353
|
/** Sticky flag: true if the sidecar /ws endpoint is unavailable. */
|
|
353
354
|
private _wsUnsupported: boolean = false;
|
|
355
|
+
private readonly _useSidecarLockBaseUrl: boolean;
|
|
354
356
|
|
|
355
357
|
constructor(exchangeName: string, options: ExchangeOptions = {}) {
|
|
356
358
|
this.exchangeName = exchangeName.toLowerCase();
|
|
@@ -370,6 +372,11 @@ export abstract class Exchange {
|
|
|
370
372
|
const baseUrl = resolved.baseUrl;
|
|
371
373
|
this.pmxtApiKey = resolved.pmxtApiKey;
|
|
372
374
|
this.isHosted = resolved.isHosted;
|
|
375
|
+
const hasBaseUrlOverride = Boolean(
|
|
376
|
+
options.baseUrl ||
|
|
377
|
+
(typeof process !== "undefined" && process.env[ENV.BASE_URL]),
|
|
378
|
+
);
|
|
379
|
+
this._useSidecarLockBaseUrl = !this.isHosted && !hasBaseUrlOverride;
|
|
373
380
|
|
|
374
381
|
// Hosted trading bridge: if the caller passed a privateKey but no
|
|
375
382
|
// explicit signer, lazily wrap it in an EthersSigner so that
|
|
@@ -417,16 +424,18 @@ export abstract class Exchange {
|
|
|
417
424
|
try {
|
|
418
425
|
await this.serverManager.ensureServerRunning();
|
|
419
426
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
427
|
+
if (this._useSidecarLockBaseUrl) {
|
|
428
|
+
// Get the actual port the server is running on
|
|
429
|
+
// (may differ from default if default port was busy)
|
|
430
|
+
const actualPort = this.serverManager.getRunningPort();
|
|
431
|
+
const newBaseUrl = `http://localhost:${actualPort}`;
|
|
424
432
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
433
|
+
// Update API client with actual base URL
|
|
434
|
+
this.config = new Configuration({
|
|
435
|
+
basePath: newBaseUrl,
|
|
436
|
+
});
|
|
437
|
+
this.api = new DefaultApi(this.config);
|
|
438
|
+
}
|
|
430
439
|
} catch (error) {
|
|
431
440
|
const msg =
|
|
432
441
|
`Failed to start PMXT server: ${error instanceof Error ? error.message : error}\n\n` +
|
|
@@ -511,12 +520,12 @@ export abstract class Exchange {
|
|
|
511
520
|
/**
|
|
512
521
|
* Resolve the current sidecar base URL.
|
|
513
522
|
*
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
*
|
|
523
|
+
* Hosted mode and explicit baseUrl clients keep the configured basePath.
|
|
524
|
+
* Default local clients re-read the lock-file port on every call so they
|
|
525
|
+
* pick up sidecar restarts that land on a different port.
|
|
517
526
|
*/
|
|
518
527
|
private resolveBaseUrl(): string {
|
|
519
|
-
if (this.isHosted) return this.config.basePath;
|
|
528
|
+
if (this.isHosted || !this._useSidecarLockBaseUrl) return this.config.basePath;
|
|
520
529
|
const port = this.serverManager.getRunningPort();
|
|
521
530
|
return `http://localhost:${port}`;
|
|
522
531
|
}
|
|
@@ -1225,7 +1234,10 @@ export abstract class Exchange {
|
|
|
1225
1234
|
const route = HOSTED_METHOD_ROUTES.get("fetchOrder")!;
|
|
1226
1235
|
const path = formatRoutePath(route, { order_id: orderId });
|
|
1227
1236
|
const data = await _tradingRequest(this, { method: route.method, path });
|
|
1228
|
-
|
|
1237
|
+
const payload = data && typeof data === "object" && "order" in data
|
|
1238
|
+
? (data as { order: unknown }).order
|
|
1239
|
+
: data;
|
|
1240
|
+
return orderFromV0(payload as Record<string, unknown>);
|
|
1229
1241
|
}
|
|
1230
1242
|
try {
|
|
1231
1243
|
const args: any[] = [];
|
|
@@ -1325,6 +1337,9 @@ export abstract class Exchange {
|
|
|
1325
1337
|
|
|
1326
1338
|
async fetchClosedOrders(params?: OrderHistoryParams): Promise<Order[]> {
|
|
1327
1339
|
await this.initPromise;
|
|
1340
|
+
if (this.isHosted) {
|
|
1341
|
+
throw new NotSupported("fetchClosedOrders is not available in hosted trading mode.");
|
|
1342
|
+
}
|
|
1328
1343
|
try {
|
|
1329
1344
|
const args: any[] = [];
|
|
1330
1345
|
if (params !== undefined) args.push(params);
|
|
@@ -1351,6 +1366,9 @@ export abstract class Exchange {
|
|
|
1351
1366
|
|
|
1352
1367
|
async fetchAllOrders(params?: OrderHistoryParams): Promise<Order[]> {
|
|
1353
1368
|
await this.initPromise;
|
|
1369
|
+
if (this.isHosted) {
|
|
1370
|
+
throw new NotSupported("fetchAllOrders is not available in hosted trading mode.");
|
|
1371
|
+
}
|
|
1354
1372
|
try {
|
|
1355
1373
|
const args: any[] = [];
|
|
1356
1374
|
if (params !== undefined) args.push(params);
|
|
@@ -1382,7 +1400,9 @@ export abstract class Exchange {
|
|
|
1382
1400
|
const route = HOSTED_METHOD_ROUTES.get("fetchPositions")!;
|
|
1383
1401
|
const path = formatRoutePath(route, { address: resolvedAddress });
|
|
1384
1402
|
const data = await _tradingRequest(this, { method: route.method, path });
|
|
1385
|
-
const list = Array.isArray(data)
|
|
1403
|
+
const list: unknown[] = Array.isArray(data)
|
|
1404
|
+
? data
|
|
1405
|
+
: (data && Array.isArray((data as any).positions) ? (data as any).positions : []);
|
|
1386
1406
|
return list.map((p) => positionFromV0(p as Record<string, unknown>));
|
|
1387
1407
|
}
|
|
1388
1408
|
try {
|
|
@@ -1416,7 +1436,9 @@ export abstract class Exchange {
|
|
|
1416
1436
|
const route = HOSTED_METHOD_ROUTES.get("fetchBalance")!;
|
|
1417
1437
|
const path = formatRoutePath(route, { address: resolvedAddress });
|
|
1418
1438
|
const data = await _tradingRequest(this, { method: route.method, path });
|
|
1419
|
-
const list = Array.isArray(data)
|
|
1439
|
+
const list: unknown[] = Array.isArray(data)
|
|
1440
|
+
? data
|
|
1441
|
+
: (data && Array.isArray((data as any).balances) ? (data as any).balances : []);
|
|
1420
1442
|
return list.map((b) => balanceFromV0(b as Record<string, unknown>));
|
|
1421
1443
|
}
|
|
1422
1444
|
try {
|
|
@@ -1990,7 +2012,13 @@ export abstract class Exchange {
|
|
|
1990
2012
|
throw this.wsTransportUnavailableError("watchAllOrderBooks");
|
|
1991
2013
|
}
|
|
1992
2014
|
|
|
1993
|
-
/**
|
|
2015
|
+
/**
|
|
2016
|
+
* Stream all orderbook updates across venues.
|
|
2017
|
+
*
|
|
2018
|
+
* @deprecated Use {@link watchAllOrderBooks} instead.
|
|
2019
|
+
* @param venues - Optional venue filter
|
|
2020
|
+
* @returns Next event with source, symbol, and orderbook
|
|
2021
|
+
*/
|
|
1994
2022
|
async firehose(venues?: string[]): Promise<FirehoseEvent> {
|
|
1995
2023
|
return this.watchAllOrderBooks(venues);
|
|
1996
2024
|
}
|
|
@@ -2127,15 +2155,18 @@ export abstract class Exchange {
|
|
|
2127
2155
|
* const order = await exchange.submitOrder(built);
|
|
2128
2156
|
*
|
|
2129
2157
|
* // Using outcome shorthand:
|
|
2158
|
+
* const yes = market.yes;
|
|
2159
|
+
* if (!yes) throw new Error("Market has no YES outcome");
|
|
2160
|
+
*
|
|
2130
2161
|
* const built2 = await exchange.buildOrder({
|
|
2131
|
-
* outcome:
|
|
2162
|
+
* outcome: yes,
|
|
2132
2163
|
* side: "buy",
|
|
2133
2164
|
* type: "market",
|
|
2134
2165
|
* amount: 10
|
|
2135
2166
|
* });
|
|
2136
2167
|
* ```
|
|
2137
2168
|
*/
|
|
2138
|
-
async buildOrder(params:
|
|
2169
|
+
async buildOrder(params: CreateOrderInput): Promise<BuiltOrder> {
|
|
2139
2170
|
if (this.isHostedTradingMode()) {
|
|
2140
2171
|
return this._hostedBuildOrder(params);
|
|
2141
2172
|
}
|
|
@@ -2289,7 +2320,7 @@ export abstract class Exchange {
|
|
|
2289
2320
|
* at submit time.
|
|
2290
2321
|
*/
|
|
2291
2322
|
private async _hostedBuildOrder(
|
|
2292
|
-
params:
|
|
2323
|
+
params: CreateOrderInput,
|
|
2293
2324
|
): Promise<BuiltOrder> {
|
|
2294
2325
|
const body = this._hostedBuildOrderBody(params);
|
|
2295
2326
|
const route = HOSTED_METHOD_ROUTES.get("buildOrder")!;
|
|
@@ -2307,7 +2338,7 @@ export abstract class Exchange {
|
|
|
2307
2338
|
/**
|
|
2308
2339
|
* Hosted-mode createOrder: build → sign → submit single-call wrapper.
|
|
2309
2340
|
*/
|
|
2310
|
-
private async _hostedCreateOrder(params:
|
|
2341
|
+
private async _hostedCreateOrder(params: CreateOrderInput): Promise<Order> {
|
|
2311
2342
|
const built = await this._hostedBuildOrder(params);
|
|
2312
2343
|
return this._hostedSubmitOrder(built);
|
|
2313
2344
|
}
|
|
@@ -2371,7 +2402,7 @@ export abstract class Exchange {
|
|
|
2371
2402
|
* `validateTypedData` / `validateEconomics`. Returns the cross-chain
|
|
2372
2403
|
* pull-leg route name for Opinion sells and Limitless cross-chain orders.
|
|
2373
2404
|
*/
|
|
2374
|
-
private _hostedTypedDataRoute(side: string, isPull: boolean):
|
|
2405
|
+
private _hostedTypedDataRoute(side: string, isPull: boolean): HostedRoute {
|
|
2375
2406
|
const venue = this.exchangeName;
|
|
2376
2407
|
const sideLower = side.toLowerCase();
|
|
2377
2408
|
if (venue === "polymarket") {
|
|
@@ -2436,10 +2467,12 @@ export abstract class Exchange {
|
|
|
2436
2467
|
return orderFromV0(data as Record<string, unknown>);
|
|
2437
2468
|
}
|
|
2438
2469
|
|
|
2439
|
-
private _hostedCancelTypedDataRoute(isPull: boolean):
|
|
2470
|
+
private _hostedCancelTypedDataRoute(isPull: boolean): HostedRoute {
|
|
2440
2471
|
const venue = this.exchangeName;
|
|
2441
2472
|
if (venue === "polymarket") return "cancel_polymarket";
|
|
2442
|
-
if (venue === "limitless")
|
|
2473
|
+
if (venue === "limitless") {
|
|
2474
|
+
return isPull ? "cancel_limitless_base_pull" : "cancel_limitless_polygon";
|
|
2475
|
+
}
|
|
2443
2476
|
return isPull ? "cancel_opinion_bsc_pull" : "cancel_opinion_polygon";
|
|
2444
2477
|
}
|
|
2445
2478
|
|
|
@@ -2449,7 +2482,7 @@ export abstract class Exchange {
|
|
|
2449
2482
|
* precision rejected via {@link to6dec}).
|
|
2450
2483
|
*/
|
|
2451
2484
|
private _hostedBuildOrderBody(
|
|
2452
|
-
params:
|
|
2485
|
+
params: CreateOrderInput,
|
|
2453
2486
|
): Record<string, unknown> {
|
|
2454
2487
|
let marketId: string | undefined = params.marketId;
|
|
2455
2488
|
let outcomeId: string | undefined = params.outcomeId;
|
|
@@ -2571,7 +2604,7 @@ export abstract class Exchange {
|
|
|
2571
2604
|
* });
|
|
2572
2605
|
* ```
|
|
2573
2606
|
*/
|
|
2574
|
-
async createOrder(params:
|
|
2607
|
+
async createOrder(params: CreateOrderInput): Promise<Order> {
|
|
2575
2608
|
// SOR escape path (preserved): legacy hosted SOR flow uses a venue-side
|
|
2576
2609
|
// SDK to execute the legs, only when a privateKey is present.
|
|
2577
2610
|
if (this.isHosted && this.exchangeName === 'sor' && this.privateKey) {
|
|
@@ -3048,6 +3081,16 @@ export interface PolymarketOptions {
|
|
|
3048
3081
|
|
|
3049
3082
|
/** Optional signature type */
|
|
3050
3083
|
signatureType?: 'eoa' | 'poly-proxy' | 'gnosis-safe' | number;
|
|
3084
|
+
|
|
3085
|
+
/**
|
|
3086
|
+
* EVM wallet address used for hosted reads/writes.
|
|
3087
|
+
*/
|
|
3088
|
+
walletAddress?: string;
|
|
3089
|
+
|
|
3090
|
+
/**
|
|
3091
|
+
* External signer used for hosted writes.
|
|
3092
|
+
*/
|
|
3093
|
+
signer?: Signer;
|
|
3051
3094
|
}
|
|
3052
3095
|
|
|
3053
3096
|
export class Polymarket extends Exchange {
|
|
@@ -3122,6 +3165,75 @@ export class Limitless extends Exchange {
|
|
|
3122
3165
|
constructor(options: ExchangeOptions = {}) {
|
|
3123
3166
|
super("limitless", options);
|
|
3124
3167
|
}
|
|
3168
|
+
|
|
3169
|
+
/**
|
|
3170
|
+
* Watch real-time AMM price updates via WebSocket.
|
|
3171
|
+
*
|
|
3172
|
+
* @param marketAddress - Limitless market contract address
|
|
3173
|
+
* @param callback - Optional callback invoked with the returned update
|
|
3174
|
+
* @returns Next price update
|
|
3175
|
+
*/
|
|
3176
|
+
async watchPrices(
|
|
3177
|
+
marketAddress: string,
|
|
3178
|
+
callback?: (data: Record<string, any>) => void,
|
|
3179
|
+
): Promise<Record<string, any>> {
|
|
3180
|
+
await this.initPromise;
|
|
3181
|
+
try {
|
|
3182
|
+
const json = await this.sidecarPostRequest("watchPrices", [marketAddress]);
|
|
3183
|
+
const data = this.handleResponse(json);
|
|
3184
|
+
callback?.(data);
|
|
3185
|
+
return data;
|
|
3186
|
+
} catch (error) {
|
|
3187
|
+
if (error instanceof PmxtError) throw error;
|
|
3188
|
+
throw new PmxtError(`Failed to watch prices: ${error}`);
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
|
|
3192
|
+
/**
|
|
3193
|
+
* Watch real-time Limitless user position updates.
|
|
3194
|
+
*
|
|
3195
|
+
* Requires API key authentication.
|
|
3196
|
+
*
|
|
3197
|
+
* @param callback - Optional callback invoked with the returned positions
|
|
3198
|
+
* @returns Next position update payload
|
|
3199
|
+
*/
|
|
3200
|
+
async watchUserPositions(
|
|
3201
|
+
callback?: (data: Position[]) => void,
|
|
3202
|
+
): Promise<Position[]> {
|
|
3203
|
+
await this.initPromise;
|
|
3204
|
+
try {
|
|
3205
|
+
const json = await this.sidecarPostRequest("watchUserPositions", []);
|
|
3206
|
+
const data = this.handleResponse(json).map(convertPosition);
|
|
3207
|
+
callback?.(data);
|
|
3208
|
+
return data;
|
|
3209
|
+
} catch (error) {
|
|
3210
|
+
if (error instanceof PmxtError) throw error;
|
|
3211
|
+
throw new PmxtError(`Failed to watch user positions: ${error}`);
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
|
|
3215
|
+
/**
|
|
3216
|
+
* Watch real-time Limitless user transaction updates.
|
|
3217
|
+
*
|
|
3218
|
+
* Requires API key authentication.
|
|
3219
|
+
*
|
|
3220
|
+
* @param callback - Optional callback invoked with the returned update
|
|
3221
|
+
* @returns Next transaction update
|
|
3222
|
+
*/
|
|
3223
|
+
async watchUserTransactions(
|
|
3224
|
+
callback?: (data: Record<string, any>) => void,
|
|
3225
|
+
): Promise<Record<string, any>> {
|
|
3226
|
+
await this.initPromise;
|
|
3227
|
+
try {
|
|
3228
|
+
const json = await this.sidecarPostRequest("watchUserTransactions", []);
|
|
3229
|
+
const data = this.handleResponse(json);
|
|
3230
|
+
callback?.(data);
|
|
3231
|
+
return data;
|
|
3232
|
+
} catch (error) {
|
|
3233
|
+
if (error instanceof PmxtError) throw error;
|
|
3234
|
+
throw new PmxtError(`Failed to watch user transactions: ${error}`);
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3125
3237
|
}
|
|
3126
3238
|
|
|
3127
3239
|
/**
|
|
@@ -3402,6 +3514,19 @@ export class Rain extends Exchange {
|
|
|
3402
3514
|
}
|
|
3403
3515
|
}
|
|
3404
3516
|
|
|
3517
|
+
|
|
3518
|
+
/**
|
|
3519
|
+
* Hunch exchange client.
|
|
3520
|
+
*
|
|
3521
|
+
* Hunch is a crypto-native prediction market. Reads are unauthenticated;
|
|
3522
|
+
* trading requires an EVM private key.
|
|
3523
|
+
*/
|
|
3524
|
+
export class Hunch extends Exchange {
|
|
3525
|
+
constructor(options: ExchangeOptions = {}) {
|
|
3526
|
+
super("hunch", options);
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
|
|
3405
3530
|
/**
|
|
3406
3531
|
* Mock exchange client.
|
|
3407
3532
|
*
|
package/pmxt/escrow.ts
CHANGED
|
@@ -16,6 +16,99 @@ import {
|
|
|
16
16
|
HostedClientLike,
|
|
17
17
|
resolveWalletAddress,
|
|
18
18
|
} from "./hosted-routing";
|
|
19
|
+
import { ValidationError } from "./errors";
|
|
20
|
+
|
|
21
|
+
const APPROVAL_TOKENS = new Set(["usdc", "ctf"]);
|
|
22
|
+
const WITHDRAW_ACTIONS = new Set(["request", "claim", "cancel"]);
|
|
23
|
+
const USDC_SCALE = 1_000_000n;
|
|
24
|
+
|
|
25
|
+
function isDecimalTokenId(token: string): boolean {
|
|
26
|
+
return /^[0-9]+$/.test(token);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function normalizeApprovalToken(token: string): string {
|
|
30
|
+
if (typeof token !== "string") {
|
|
31
|
+
throw new ValidationError(
|
|
32
|
+
"token must be 'usdc', 'ctf', or a decimal CTF token_id string",
|
|
33
|
+
"token",
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const candidate = token.trim();
|
|
38
|
+
const normalized = candidate.toLowerCase();
|
|
39
|
+
if (APPROVAL_TOKENS.has(normalized)) {
|
|
40
|
+
return normalized;
|
|
41
|
+
}
|
|
42
|
+
if (isDecimalTokenId(candidate)) {
|
|
43
|
+
return candidate;
|
|
44
|
+
}
|
|
45
|
+
throw new ValidationError(
|
|
46
|
+
"token must be 'usdc', 'ctf', or a decimal CTF token_id string",
|
|
47
|
+
"token",
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function normalizeAmountWei(amountWei: bigint | undefined): string | undefined {
|
|
52
|
+
if (amountWei === undefined) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
if (typeof amountWei !== "bigint") {
|
|
56
|
+
throw new ValidationError("amount_wei must be a non-negative integer", "amount_wei");
|
|
57
|
+
}
|
|
58
|
+
if (amountWei < 0n) {
|
|
59
|
+
throw new ValidationError("amount_wei must be non-negative", "amount_wei");
|
|
60
|
+
}
|
|
61
|
+
return amountWei.toString();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function validateUsdcDecimal(value: string, field: string): void {
|
|
65
|
+
const match = /^([0-9]+)(?:\.([0-9]+))?$/.exec(value);
|
|
66
|
+
if (!match) {
|
|
67
|
+
throw new ValidationError(`${field} must be a finite positive number`, field);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const fractional = match[2] ?? "";
|
|
71
|
+
if (fractional.length > 6) {
|
|
72
|
+
throw new ValidationError(
|
|
73
|
+
`${field} precision exceeds 6 decimals; max precision for USDC is 0.000001`,
|
|
74
|
+
field,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const scaled = BigInt(match[1]) * USDC_SCALE + BigInt(fractional.padEnd(6, "0"));
|
|
79
|
+
if (scaled <= 0n) {
|
|
80
|
+
throw new ValidationError(`${field} must be positive`, field);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function normalizeUsdcAmount(value: number | string | bigint, field: string = "amount"): number | string {
|
|
85
|
+
if (typeof value === "bigint") {
|
|
86
|
+
if (value <= 0n) {
|
|
87
|
+
throw new ValidationError(`${field} must be positive`, field);
|
|
88
|
+
}
|
|
89
|
+
return value.toString();
|
|
90
|
+
}
|
|
91
|
+
if (typeof value === "number") {
|
|
92
|
+
if (!Number.isFinite(value)) {
|
|
93
|
+
throw new ValidationError(`${field} must be a finite positive number`, field);
|
|
94
|
+
}
|
|
95
|
+
validateUsdcDecimal(String(value), field);
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
if (typeof value === "string") {
|
|
99
|
+
const trimmed = value.trim();
|
|
100
|
+
validateUsdcDecimal(trimmed, field);
|
|
101
|
+
return trimmed;
|
|
102
|
+
}
|
|
103
|
+
throw new ValidationError(`${field} must be a finite positive number`, field);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function normalizeWithdrawAction(action: string): "request" | "claim" | "cancel" {
|
|
107
|
+
if (typeof action === "string" && WITHDRAW_ACTIONS.has(action)) {
|
|
108
|
+
return action as "request" | "claim" | "cancel";
|
|
109
|
+
}
|
|
110
|
+
throw new ValidationError("action must be 'request', 'claim', or 'cancel'", "action");
|
|
111
|
+
}
|
|
19
112
|
|
|
20
113
|
export class Escrow {
|
|
21
114
|
constructor(private readonly client: HostedClientLike) {}
|
|
@@ -25,13 +118,16 @@ export class Escrow {
|
|
|
25
118
|
* `amountWei` is omitted, the server returns an unlimited approval.
|
|
26
119
|
*/
|
|
27
120
|
async approveTx(token: string, amountWei?: bigint): Promise<unknown> {
|
|
121
|
+
const address = resolveWalletAddress(this.client);
|
|
122
|
+
const approvalAmount = normalizeAmountWei(amountWei);
|
|
28
123
|
const route = HOSTED_METHOD_ROUTES.get("escrowApproveTx")!;
|
|
29
124
|
return _tradingRequest(this.client, {
|
|
30
125
|
method: route.method,
|
|
31
126
|
path: route.path,
|
|
32
127
|
body: {
|
|
33
|
-
token,
|
|
34
|
-
|
|
128
|
+
token: normalizeApprovalToken(token),
|
|
129
|
+
user_address: address,
|
|
130
|
+
...(approvalAmount === undefined ? {} : { amount_wei: approvalAmount }),
|
|
35
131
|
},
|
|
36
132
|
});
|
|
37
133
|
}
|
|
@@ -41,12 +137,15 @@ export class Escrow {
|
|
|
41
137
|
* grid). Accepts number, decimal string, or BigInt in micro-units.
|
|
42
138
|
*/
|
|
43
139
|
async depositTx(amount: number | string | bigint): Promise<unknown> {
|
|
140
|
+
const address = resolveWalletAddress(this.client);
|
|
44
141
|
const route = HOSTED_METHOD_ROUTES.get("escrowDepositTx")!;
|
|
45
142
|
return _tradingRequest(this.client, {
|
|
46
143
|
method: route.method,
|
|
47
144
|
path: route.path,
|
|
48
145
|
body: {
|
|
49
|
-
|
|
146
|
+
token: "usdc",
|
|
147
|
+
amount: normalizeUsdcAmount(amount),
|
|
148
|
+
user_address: address,
|
|
50
149
|
},
|
|
51
150
|
});
|
|
52
151
|
}
|
|
@@ -60,17 +159,34 @@ export class Escrow {
|
|
|
60
159
|
action: "request" | "claim" | "cancel",
|
|
61
160
|
amount?: number | string | bigint,
|
|
62
161
|
): Promise<unknown> {
|
|
162
|
+
const normalizedAction = normalizeWithdrawAction(action);
|
|
163
|
+
const address = resolveWalletAddress(this.client);
|
|
63
164
|
const route = HOSTED_METHOD_ROUTES.get("escrowWithdrawTx")!;
|
|
64
|
-
|
|
65
|
-
amount === undefined
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
165
|
+
if (normalizedAction === "request") {
|
|
166
|
+
if (amount === undefined) {
|
|
167
|
+
throw new ValidationError("amount is required when action='request'", "amount");
|
|
168
|
+
}
|
|
169
|
+
return _tradingRequest(this.client, {
|
|
170
|
+
method: route.method,
|
|
171
|
+
path: route.path,
|
|
172
|
+
body: {
|
|
173
|
+
action: normalizedAction,
|
|
174
|
+
token: "usdc",
|
|
175
|
+
amount: normalizeUsdcAmount(amount),
|
|
176
|
+
user_address: address,
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (amount !== undefined) {
|
|
181
|
+
throw new ValidationError(
|
|
182
|
+
`amount must be omitted when action='${normalizedAction}'`,
|
|
183
|
+
"amount",
|
|
184
|
+
);
|
|
185
|
+
}
|
|
70
186
|
return _tradingRequest(this.client, {
|
|
71
187
|
method: route.method,
|
|
72
188
|
path: route.path,
|
|
73
|
-
body: { action,
|
|
189
|
+
body: { action: normalizedAction, token: "usdc", user_address: address },
|
|
74
190
|
});
|
|
75
191
|
}
|
|
76
192
|
|
|
@@ -82,12 +198,16 @@ export class Escrow {
|
|
|
82
198
|
opts: { include?: string; address?: string } = {},
|
|
83
199
|
): Promise<unknown> {
|
|
84
200
|
const address = resolveWalletAddress(this.client, opts.address);
|
|
201
|
+
const include = (opts.include ?? "pending,events").trim();
|
|
202
|
+
if (!include) {
|
|
203
|
+
throw new ValidationError("include must not be empty", "include");
|
|
204
|
+
}
|
|
85
205
|
const route = HOSTED_METHOD_ROUTES.get("escrowWithdrawals")!;
|
|
86
206
|
const path = formatRoutePath(route, { address });
|
|
87
207
|
return _tradingRequest(this.client, {
|
|
88
208
|
method: route.method,
|
|
89
209
|
path,
|
|
90
|
-
params: { include
|
|
210
|
+
params: { include },
|
|
91
211
|
});
|
|
92
212
|
}
|
|
93
213
|
}
|
|
@@ -141,7 +141,9 @@ export type HostedRoute =
|
|
|
141
141
|
| "opinion_sell_bsc_pull"
|
|
142
142
|
| "cancel_polymarket"
|
|
143
143
|
| "cancel_opinion_polygon"
|
|
144
|
-
| "cancel_opinion_bsc_pull"
|
|
144
|
+
| "cancel_opinion_bsc_pull"
|
|
145
|
+
| "cancel_limitless_polygon"
|
|
146
|
+
| "cancel_limitless_base_pull";
|
|
145
147
|
|
|
146
148
|
const SCHEMAS: Readonly<Record<HostedRoute, TypedDataSchema>> = {
|
|
147
149
|
polymarket_buy: {
|
|
@@ -221,6 +223,20 @@ const SCHEMAS: Readonly<Record<HostedRoute, TypedDataSchema>> = {
|
|
|
221
223
|
messageKeys: messageKeysFromFields(CANCEL_PULL_FIELDS),
|
|
222
224
|
walletField: "user",
|
|
223
225
|
},
|
|
226
|
+
cancel_limitless_polygon: {
|
|
227
|
+
primaryType: "CancelOrder",
|
|
228
|
+
domain: PREFUNDED_DOMAIN,
|
|
229
|
+
fields: CANCEL_ORDER_FIELDS,
|
|
230
|
+
messageKeys: messageKeysFromFields(CANCEL_ORDER_FIELDS),
|
|
231
|
+
walletField: "user",
|
|
232
|
+
},
|
|
233
|
+
cancel_limitless_base_pull: {
|
|
234
|
+
primaryType: "CancelPull",
|
|
235
|
+
domain: LIMITLESS_VENUE_DOMAIN,
|
|
236
|
+
fields: CANCEL_PULL_FIELDS,
|
|
237
|
+
messageKeys: messageKeysFromFields(CANCEL_PULL_FIELDS),
|
|
238
|
+
walletField: "user",
|
|
239
|
+
},
|
|
224
240
|
};
|
|
225
241
|
|
|
226
242
|
// secp256k1 group order / 2, for canonical low-s check.
|
package/pmxt/models.ts
CHANGED
|
@@ -549,6 +549,12 @@ export interface CreateOrderParams {
|
|
|
549
549
|
/** Limit price (required for limit orders, 0.0-1.0) */
|
|
550
550
|
price?: number;
|
|
551
551
|
|
|
552
|
+
/** Hosted mode: unit that amount is denominated in. Market buys use usdc; market sells and limit orders use shares. */
|
|
553
|
+
denom?: "usdc" | "shares";
|
|
554
|
+
|
|
555
|
+
/** Hosted mode: maximum slippage percentage for market orders. */
|
|
556
|
+
slippage_pct?: number;
|
|
557
|
+
|
|
552
558
|
/** Optional fee rate (e.g., 1000 for 0.1%) */
|
|
553
559
|
fee?: number;
|
|
554
560
|
|
|
@@ -562,6 +568,20 @@ export interface CreateOrderParams {
|
|
|
562
568
|
onBehalfOf?: number;
|
|
563
569
|
}
|
|
564
570
|
|
|
571
|
+
/**
|
|
572
|
+
* Public order input accepted by createOrder/buildOrder.
|
|
573
|
+
*
|
|
574
|
+
* Callers can either pass explicit marketId/outcomeId fields, or pass an
|
|
575
|
+
* outcome object returned by fetchMarkets and let the SDK infer those ids.
|
|
576
|
+
*/
|
|
577
|
+
export type CreateOrderInput =
|
|
578
|
+
| (CreateOrderParams & { outcome?: never })
|
|
579
|
+
| (Omit<CreateOrderParams, 'marketId' | 'outcomeId'> & {
|
|
580
|
+
outcome: MarketOutcome;
|
|
581
|
+
marketId?: never;
|
|
582
|
+
outcomeId?: never;
|
|
583
|
+
});
|
|
584
|
+
|
|
565
585
|
/** Alias matching the core MarketFetchParams name. */
|
|
566
586
|
export type MarketFetchParams = MarketFilterParams;
|
|
567
587
|
|
package/pmxt/server-manager.ts
CHANGED
|
@@ -103,13 +103,15 @@ export class ServerManager {
|
|
|
103
103
|
* Check if the server is running.
|
|
104
104
|
*/
|
|
105
105
|
async isServerRunning(): Promise<boolean> {
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
const info = this.getServerInfo();
|
|
107
|
+
if (!info?.port) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
108
110
|
|
|
109
111
|
try {
|
|
110
112
|
// Use native fetch to check health on the actual running port
|
|
111
113
|
// This avoids issues where this.api is configured with the wrong port
|
|
112
|
-
const response = await fetch(`http://localhost:${port}/health`, {
|
|
114
|
+
const response = await fetch(`http://localhost:${info.port}/health`, {
|
|
113
115
|
signal: AbortSignal.timeout(5_000),
|
|
114
116
|
});
|
|
115
117
|
if (response.ok) {
|